From 5c96ec90d4e75b0b87ff7233c5da9eed15e05bea Mon Sep 17 00:00:00 2001 From: Artur Signell Date: Tue, 24 Feb 2026 13:41:27 +0200 Subject: [PATCH 01/16] feat: Set up new OSS browserless-test module structure (#2139) Add Apache 2.0 license header template, parent aggregator POM, and child POM files for three new open-source browserless test modules under browserless-test/: - browserless-test/bom - browserless-test/shared - browserless-test/junit6 - browserless-test/quarkus Include duplicates of eclipse formatter config and import order files so the browserless-test directory can be extracted as a standalone project with working spotless configuration. Register the parent module in root POM. No source code yet. --- browserless-test/bom/pom.xml | 59 ++++ .../eclipse/VaadinJavaConventions.xml | 283 ++++++++++++++++ browserless-test/eclipse/flow.importorder | 10 + browserless-test/junit6/pom.xml | 318 +++++++++++++++++ browserless-test/pom.xml | 168 +++++++++ browserless-test/quarkus/pom.xml | 145 ++++++++ browserless-test/shared/pom.xml | 320 ++++++++++++++++++ .../src/license/apache2/header.txt | 15 + pom.xml | 1 + src/license/apache2/header.txt | 15 + 10 files changed, 1334 insertions(+) create mode 100644 browserless-test/bom/pom.xml create mode 100644 browserless-test/eclipse/VaadinJavaConventions.xml create mode 100644 browserless-test/eclipse/flow.importorder create mode 100644 browserless-test/junit6/pom.xml create mode 100644 browserless-test/pom.xml create mode 100644 browserless-test/quarkus/pom.xml create mode 100644 browserless-test/shared/pom.xml create mode 100644 browserless-test/src/license/apache2/header.txt create mode 100644 src/license/apache2/header.txt diff --git a/browserless-test/bom/pom.xml b/browserless-test/bom/pom.xml new file mode 100644 index 000000000..d2e34c154 --- /dev/null +++ b/browserless-test/bom/pom.xml @@ -0,0 +1,59 @@ + + + 4.0.0 + + com.vaadin + vaadin-parent + 3.0.0 + + + browserless-test-bom + pom + 1.0-SNAPSHOT + Vaadin Browserless Test (Bill of Materials) + Vaadin Browserless Test (Bill of Materials) + http://vaadin.com + 2022 + + + + Apache License, Version 2.0 + https://www.apache.org/licenses/LICENSE-2.0 + + + + + + + com.diffplug.spotless + spotless-maven-plugin + 3.2.1 + + true + + + + + + + + + com.vaadin + browserless-test-shared + ${project.version} + + + com.vaadin + browserless-test-junit6 + ${project.version} + + + com.vaadin + browserless-test-quarkus + ${project.version} + + + + diff --git a/browserless-test/eclipse/VaadinJavaConventions.xml b/browserless-test/eclipse/VaadinJavaConventions.xml new file mode 100644 index 000000000..a06c9151c --- /dev/null +++ b/browserless-test/eclipse/VaadinJavaConventions.xml @@ -0,0 +1,283 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/browserless-test/eclipse/flow.importorder b/browserless-test/eclipse/flow.importorder new file mode 100644 index 000000000..0f03d17f5 --- /dev/null +++ b/browserless-test/eclipse/flow.importorder @@ -0,0 +1,10 @@ +#Organize Import Order +#Fri Nov 10 13:24:02 EET 2017 +7=\# +6=elemental +5=com.vaadin +4=com.google.gwt +3= +2=java +1=javax +0=jakarta diff --git a/browserless-test/junit6/pom.xml b/browserless-test/junit6/pom.xml new file mode 100644 index 000000000..460d4417b --- /dev/null +++ b/browserless-test/junit6/pom.xml @@ -0,0 +1,318 @@ + + + 4.0.0 + + com.vaadin + browserless-test + 1.0-SNAPSHOT + + browserless-test-junit6 + jar + Vaadin Browserless Test JUnit6 + Vaadin Browserless UI Test - JUnit 6 + + + + dokka-javadocs + + + testbench.javadocs + + + + true + + + + + org.jetbrains.dokka + dokka-maven-plugin + + + package + + javadoc + + + + + + + + + release + + + + org.jetbrains.dokka + dokka-maven-plugin + + + package + + javadocJar + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + true + + true + + true + + + + 1 + + + + + + + + + + + + + src/main/resources + true + + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + + + compile + + compile + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/main/java + + + + + test-compile + + test-compile + + + + ${project.basedir}/src/test/kotlin + ${project.basedir}/src/test/java + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + default-compile + none + + + + default-testCompile + none + + + java-compile + compile + + compile + + + + java-test-compile + test-compile + + testCompile + + + + + + org.jetbrains.dokka + dokka-maven-plugin + + ${maven.compiler.source} + + ${project.basedir}/src/main/java + ${project.basedir}/src/main/kotlin + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + add-kotlin-sources-for-source-jar + package + + add-source + + + + src/main/kotlin + + + + + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + org.apache.maven.plugins + maven-failsafe-plugin + + + + integration-test + verify + + + + + + + + + + + com.vaadin + vaadin + ${vaadin.version} + provided + + + com.vaadin + vaadin-spring + ${flow.version} + provided + true + + + + com.github.mvysny.dynatest + dynatest + 0.25 + test + + + com.github.mvysny.karibudsl + karibu-dsl + 1.0.8 + test + + + jakarta.annotation + jakarta.annotation-api + 3.0.0 + test + + + com.vaadin + browserless-test-shared + ${project.version} + + + org.junit.jupiter + junit-jupiter + + + org.junit.platform + junit-platform-engine + + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + ${kotlin.version} + + + org.jetbrains.kotlin + kotlin-reflect + ${kotlin.version} + + + + + org.springframework + spring-test + provided + true + + + org.springframework.security + spring-security-core + provided + true + + + org.springframework.security + spring-security-test + provided + true + + + org.slf4j + slf4j-simple + test + + + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring.boot.version} + pom + import + + + + + diff --git a/browserless-test/pom.xml b/browserless-test/pom.xml new file mode 100644 index 000000000..24801ceb3 --- /dev/null +++ b/browserless-test/pom.xml @@ -0,0 +1,168 @@ + + + 4.0.0 + + com.vaadin + vaadin-parent + 3.0.0 + + + browserless-test + pom + 1.0-SNAPSHOT + Vaadin Browserless Test + http://vaadin.com + 2022 + + + + Apache License, Version 2.0 + https://www.apache.org/licenses/LICENSE-2.0 + + + + + shared + junit6 + quarkus + bom + + + + 21 + 4.0.0 + 25.1-SNAPSHOT + 25.1-SNAPSHOT + 2.0.17 + 3.5.5 + 3.5.5 + 3.2.1 + 2.2.20 + 2.1.0 + 3.14.0 + 3.3.1 + 3.4.2 + 3.11.2 + 3.6.1 + ${skipTests} + + + + + central + https://repo.maven.apache.org/maven2 + + false + + + + vaadin-prereleases + https://maven.vaadin.com/vaadin-prereleases + + true + + + true + + + + + + central + https://repo.maven.apache.org/maven2 + + false + + + + vaadin-prereleases + https://maven.vaadin.com/vaadin-prereleases + + true + + + true + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${surefire.version} + + ${skipUnitTests} + + + + org.apache.maven.plugins + maven-failsafe-plugin + ${failsafe.version} + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven.compiler.plugin.version} + + + org.jetbrains.dokka + dokka-maven-plugin + ${dokka.version} + + + org.codehaus.mojo + build-helper-maven-plugin + ${build.helper.plugin.version} + + + org.apache.maven.plugins + maven-source-plugin + ${maven.source.plugin.version} + + + org.apache.maven.plugins + maven-jar-plugin + ${maven.jar.plugin.version} + + + org.apache.maven.plugins + maven-javadoc-plugin + ${maven.javadoc.plugin.version} + + + + + + com.diffplug.spotless + spotless-maven-plugin + ${spotless.plugin.version} + + + + ${maven.multiModuleProjectDirectory}/eclipse/VaadinJavaConventions.xml + + + + + ${maven.multiModuleProjectDirectory}/eclipse/flow.importorder + + + + ${maven.multiModuleProjectDirectory}/src/license/apache2/header.txt + + + + + + + diff --git a/browserless-test/quarkus/pom.xml b/browserless-test/quarkus/pom.xml new file mode 100644 index 000000000..2af338e17 --- /dev/null +++ b/browserless-test/quarkus/pom.xml @@ -0,0 +1,145 @@ + + + 4.0.0 + + com.vaadin + browserless-test + 1.0-SNAPSHOT + + browserless-test-quarkus + jar + Vaadin Browserless Test for Quarkus + Vaadin Browserless UI Test - Quarkus + + + + 3.24.0 + + + + + release + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + true + + true + + true + + + + 1 + + + + + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + + + + io.quarkus + quarkus-bom + ${quarkus.version} + pom + import + + + + + + com.vaadin + vaadin + ${vaadin.version} + provided + + + com.vaadin + vaadin-quarkus-extension + ${vaadin.version} + provided + + + com.vaadin + browserless-test-junit6 + ${project.version} + + + io.quarkus + quarkus-junit5 + test + + + io.quarkus + quarkus-test-security + test + + + org.junit.jupiter + junit-jupiter + + + org.junit.platform + junit-platform-engine + + + + + diff --git a/browserless-test/shared/pom.xml b/browserless-test/shared/pom.xml new file mode 100644 index 000000000..0bb468434 --- /dev/null +++ b/browserless-test/shared/pom.xml @@ -0,0 +1,320 @@ + + + 4.0.0 + + com.vaadin + browserless-test + 1.0-SNAPSHOT + + browserless-test-shared + jar + Vaadin Browserless Test Shared + Vaadin Browserless UI Test - Shared + + + + dokka-javadocs + + + testbench.javadocs + + + + true + + + + + org.jetbrains.dokka + dokka-maven-plugin + + + package + + javadoc + + + + + + + + + release + + + + org.jetbrains.dokka + dokka-maven-plugin + + + package + + javadocJar + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + true + + true + + true + + + + 1 + + + + + + + + + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + + + compile + + compile + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/main/java + + + + + test-compile + + test-compile + + + + ${project.basedir}/src/test/kotlin + ${project.basedir}/src/test/java + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + default-compile + none + + + + default-testCompile + none + + + java-compile + compile + + compile + + + + java-test-compile + test-compile + + testCompile + + + + + + org.jetbrains.dokka + dokka-maven-plugin + + ${maven.compiler.source} + + ${project.basedir}/src/main/java + ${project.basedir}/src/main/kotlin + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + add-kotlin-sources-for-source-jar + package + + add-source + + + + src/main/kotlin + + + + + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + org.apache.maven.plugins + maven-failsafe-plugin + + + + integration-test + verify + + + + + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring.boot.version} + pom + import + + + + + + org.slf4j + slf4j-api + ${slf4j.version} + + + + org.mockito + mockito-core + 5.20.0 + test + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + ${kotlin.version} + + + org.jetbrains.kotlin + kotlin-reflect + ${kotlin.version} + + + io.github.classgraph + classgraph + 4.8.184 + + + + com.vaadin + vaadin + ${vaadin.version} + provided + + + com.vaadin + vaadin-spring + ${flow.version} + provided + true + + + + com.vaadin + flow-polymer-template + ${flow.version} + provided + true + + + + com.github.mvysny.dynatest + dynatest + 0.25 + test + + + com.github.mvysny.karibudsl + karibu-dsl + 1.0.8 + test + + + + jakarta.servlet + jakarta.servlet-api + 6.1.0 + + + + + org.springframework + spring-test + provided + true + + + org.springframework.security + spring-security-core + provided + true + + + org.springframework.security + spring-security-test + provided + true + + + tools.jackson.core + jackson-databind + 3.0.4 + + + + + diff --git a/browserless-test/src/license/apache2/header.txt b/browserless-test/src/license/apache2/header.txt new file mode 100644 index 000000000..2771e4257 --- /dev/null +++ b/browserless-test/src/license/apache2/header.txt @@ -0,0 +1,15 @@ +/** + * Copyright (C) 2000-$YEAR Vaadin Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ diff --git a/pom.xml b/pom.xml index 20a2dc9db..3f1733658 100644 --- a/pom.xml +++ b/pom.xml @@ -55,6 +55,7 @@ vaadin-testbench-core vaadin-testbench-core-junit5 vaadin-testbench-core-junit6 + browserless-test vaadin-testbench-unit-shared vaadin-testbench-unit vaadin-testbench-unit-junit5 diff --git a/src/license/apache2/header.txt b/src/license/apache2/header.txt new file mode 100644 index 000000000..2771e4257 --- /dev/null +++ b/src/license/apache2/header.txt @@ -0,0 +1,15 @@ +/** + * Copyright (C) 2000-$YEAR Vaadin Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ From 8ed8d2b501bec76209d9242de034faf9840ba066 Mon Sep 17 00:00:00 2001 From: Artur Signell Date: Tue, 24 Feb 2026 11:02:06 +0000 Subject: [PATCH 02/16] feat: Copy source files into new OSS browserless-test modules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Copy Java/Kotlin sources from vaadin-testbench-unit-* into the new modules, preserving original relative paths and content: - browserless-test/shared ← vaadin-testbench-unit-shared - browserless-test/junit6 ← vaadin-testbench-unit-junit5 - browserless-test/quarkus ← vaadin-testbench-unit-quarkus 281 new files — byte-for-byte identical to their originals. --- .../testbench/unit/SpringUIUnitTest.java | 76 + .../unit/TreeOnFailureExtension.java | 39 + .../com/vaadin/testbench/unit/UIUnitTest.java | 91 ++ .../test/java/com/example/SingleParam.java | 26 + .../test/java/com/example/TemplatedParam.java | 26 + .../test/java/com/example/base/ErrorView.java | 27 + .../java/com/example/base/HelloWorldView.java | 20 + .../com/example/base/ParametrizedView.java | 29 + .../java/com/example/base/ParentView.java | 17 + .../java/com/example/base/WelcomeView.java | 25 + .../com/example/base/child/ChildView.java | 19 + .../navigation/NavigationPostponeView.java | 34 + .../com/example/base/signals/SignalsView.java | 79 + .../com/testapp/MyRouteNotFoundError.java | 28 + .../java/com/testapp/security/LoginView.java | 19 + .../com/testapp/security/ProtectedView.java | 22 + .../accordion/AccordionTesterTest.java | 68 + .../component/accordion/AccordionView.java | 43 + .../component/button/ButtonTesterTest.java | 131 ++ .../flow/component/button/ButtonView.java | 25 + .../charts/ChartTesterSeriesValueTest.java | 344 ++++ .../component/charts/ChartTesterTest.java | 276 ++++ .../component/charts/ColumnChartView.java | 69 + .../checkbox/CheckboxGroupTesterTest.java | 187 +++ .../checkbox/CheckboxTesterTest.java | 99 ++ .../flow/component/checkbox/CheckboxView.java | 58 + .../combobox/ComboBoxTesterTest.java | 70 + .../flow/component/combobox/ComboBoxView.java | 49 + .../MultiSelectComboBoxTesterTest.java | 78 + .../combobox/MultiSelectComboBoxView.java | 50 + .../ConfirmDialogTesterTest.java | 130 ++ .../confirmdialog/ConfirmDialogView.java | 25 + .../contextmenu/ContextMenuTesterTest.java | 385 +++++ .../contextmenu/ContextMenuView.java | 74 + .../datepicker/DatePickerTesterTest.java | 72 + .../component/datepicker/DatePickerView.java | 25 + .../DateTimePickerTesterTest.java | 78 + .../datetimepicker/DateTimePickerView.java | 25 + .../component/details/DetailsTesterTest.java | 136 ++ .../flow/component/details/DetailsView.java | 33 + .../component/dialog/DialogTesterTest.java | 91 ++ .../flow/component/dialog/DialogView.java | 29 + .../vaadin/flow/component/grid/Address.java | 83 + .../component/grid/BasicGridTesterTest.java | 249 +++ .../flow/component/grid/BasicGridView.java | 73 + .../component/grid/BeanGridTesterTest.java | 60 + .../flow/component/grid/BeanGridView.java | 30 + .../vaadin/flow/component/grid/CheckBox.java | 29 + .../vaadin/flow/component/grid/Country.java | 33 + .../grid/GetTextCellRendererTest.java | 52 + .../component/grid/GridTesterSortTest.java | 263 +++ .../vaadin/flow/component/grid/Person.java | 184 +++ .../flow/component/grid/RendererGridView.java | 68 + .../flow/component/grid/SortGridView.java | 49 + .../html/testbench/AnchorTesterTest.java | 167 ++ .../component/html/testbench/AnchorView.java | 19 + .../html/testbench/RangeInputTesterTest.java | 204 +++ .../html/testbench/RangeInputView.java | 29 + .../component/listbox/ListBoxTesterTest.java | 58 + .../flow/component/listbox/ListBoxView.java | 36 + .../listbox/MultiSelectListBoxTesterTest.java | 92 ++ .../component/login/LoginFormTesterTest.java | 71 + .../flow/component/login/LoginFormView.java | 44 + .../login/LoginOverlayTesterTest.java | 84 + .../component/login/LoginOverlayView.java | 47 + .../component/menubar/MenuBarTesterTest.java | 260 +++ .../flow/component/menubar/MenuBarView.java | 72 + .../messages/MessageInputTesterTest.java | 69 + .../messages/MessageListTesterTest.java | 126 ++ .../flow/component/messages/MessagesView.java | 40 + .../notification/NotificationTesterTest.java | 182 +++ .../notification/NotificationView.java | 19 + .../RadioButtonGroupTesterTest.java | 82 + .../radiobutton/RadioButtonTesterTest.java | 81 + .../radiobutton/RadioButtonView.java | 55 + .../routerlink/AbstractTargetView.java | 23 + .../RouterLinkQueryParameterTargetView.java | 39 + .../RouterLinkRouteParameterTargetView.java | 39 + .../RouterLinkStaticTargetView.java | 25 + .../routerlink/RouterLinkTesterTest.java | 150 ++ .../RouterLinkUrlParameterTargetView.java | 31 + .../component/routerlink/RouterLinkView.java | 71 + .../component/select/SelectTesterTest.java | 78 + .../flow/component/select/SelectView.java | 56 + .../component/sidenav/SideNavTesterTest.java | 322 ++++ .../flow/component/sidenav/SideNavView.java | 66 + .../flow/component/sidenav/TargetView.java | 29 + .../component/tabs/TabSheetTesterTest.java | 258 +++ .../flow/component/tabs/TabSheetView.java | 37 + .../flow/component/tabs/TabsTesterTest.java | 209 +++ .../vaadin/flow/component/tabs/TabsView.java | 33 + .../textfield/NumberFieldTesterTest.java | 136 ++ .../component/textfield/NumberFieldView.java | 29 + .../textfield/TextAreaTesterTest.java | 140 ++ .../component/textfield/TextAreaView.java | 26 + .../textfield/TextFieldTesterTest.java | 224 +++ .../component/textfield/TextFieldView.java | 25 + .../timepicker/TimePickerTesterTest.java | 74 + .../component/timepicker/TimePickerView.java | 25 + .../AssertingTransferProgressListener.java | 160 ++ .../upload/UploadDeprecatedAPIView.java | 35 + .../upload/UploadTesterDeprecatedAPITest.java | 355 ++++ .../component/upload/UploadTesterTest.java | 201 +++ .../flow/component/upload/UploadView.java | 30 + ...lbackLitRendererVirtualListTesterTest.java | 144 ++ .../CallbackLitRendererVirtualListView.java | 102 ++ ...omponentRendererVirtualListTesterTest.java | 168 ++ .../ComponentRendererVirtualListView.java | 99 ++ .../flow/component/virtuallist/User.java | 103 ++ .../flow/component/virtuallist/UserData.java | 68 + .../ValueProviderVirtualListTesterTest.java | 139 ++ .../ValueProviderVirtualListView.java | 58 + .../NoClassDefFoundComponent.java | 16 + .../NoClassDefFoundComponentTester.java | 26 + .../TypeNotPresentComponent.java | 16 + .../TypeNotPresentComponentTester.java | 27 + .../vaadin/testbench/unit/ClickableTest.java | 197 +++ .../testbench/unit/ComponentQueryTest.java | 1437 +++++++++++++++++ .../testbench/unit/ComponentTesterTest.java | 240 +++ .../testbench/unit/ElementConditionsTest.java | 325 ++++ .../testbench/unit/NonGenericTestTester.java | 24 + .../testbench/unit/SecurityTestConfig.java | 102 ++ .../vaadin/testbench/unit/SignalsTest.java | 84 + .../unit/SpringUIUnitBaseClassTest.java | 53 + .../unit/SpringUnitSecurityTest.java | 124 ++ .../vaadin/testbench/unit/TestComponent.java | 16 + .../unit/TestComponentForConcreteTester.java | 16 + .../unit/TestCustomInstantiatorFactory.java | 26 + .../com/vaadin/testbench/unit/TestTester.java | 23 + .../testbench/unit/TesterResolutionTest.java | 81 + .../vaadin/testbench/unit/TesterScanTest.java | 44 + .../testbench/unit/UIUnitBaseClassTest.java | 118 ++ .../testbench/unit/UIUnitNavigationTest.java | 74 + .../testbench/unit/UIUnitShortcutTest.java | 133 ++ ...overRoutesInAnnotatedClassPackageTest.java | 35 + .../ViewPackagesTestView.java | 18 + .../DiscoverRoutesInPackageByClassTest.java | 38 + .../DiscoverRoutesInPackageByNameTest.java | 36 + ...overRoutesInPackageByClassAndNameTest.java | 38 + .../vaadin/testbench/unit/TestInitListener.kt | 38 + .../com/vaadin/testbench/unit/TestUtils.kt | 58 + .../testbench/unit/internal/AllTests.kt | 84 + .../testbench/unit/internal/AsyncTest.kt | 154 ++ .../unit/internal/AttachedTextField.kt | 22 + .../testbench/unit/internal/BasicUtilsTest.kt | 117 ++ .../unit/internal/ComponentUtilsTest.kt | 274 ++++ .../testbench/unit/internal/CompositeTest.kt | 38 + .../internal/DepthFirstTreeIteratorTest.kt | 48 + .../unit/internal/ElementUtilsTest.kt | 111 ++ .../testbench/unit/internal/LocatorTest.kt | 422 +++++ .../testbench/unit/internal/MockVaadinTest.kt | 556 +++++++ .../unit/internal/PrettyPrintTreeTest.kt | 287 ++++ .../testbench/unit/internal/RoutesTest.kt | 117 ++ .../testbench/unit/internal/SearchSpecTest.kt | 153 ++ .../testbench/unit/internal/ShortcutsTest.kt | 146 ++ .../testbench/unit/mocks/MockContextTest.kt | 63 + .../unit/mocks/MockHttpSessionTest.kt | 94 ++ .../testbench/unit/mocks/MockRequestTest.kt | 97 ++ .../testbench/unit/mocks/MockResponseTest.kt | 44 + .../unit/mocks/SessionAttributeMapTest.kt | 122 ++ .../com/vaadin/testbench/unit/mocks/Utils.kt | 18 + ...adin.flow.server.VaadinServiceInitListener | 1 + .../webapp/VAADIN/themes/default/img/1.txt | 0 .../quarkus/QuarkusSecurityCustomizer.java | 47 + .../quarkus/QuarkusTestLookupInitializer.java | 52 + .../unit/quarkus/QuarkusUIUnitTest.java | 79 + .../quarkus/mocks/MockQuarkusServlet.java | 95 ++ .../mocks/MockQuarkusServletService.java | 78 + .../java/com/example/base/DummyService.java | 15 + .../java/com/example/base/HelloWorldView.java | 24 + .../java/com/example/base/WelcomeView.java | 30 + .../com/testapp/security/AnyRoleView.java | 27 + .../java/com/testapp/security/LoginView.java | 19 + .../com/testapp/security/ProtectedView.java | 22 + .../testapp/security/RoleRestrictedView.java | 22 + .../QuarkusSecurityCustomizerTest.java | 74 + .../quarkus/QuarkusUIUnitBaseClassTest.java | 57 + .../unit/quarkus/QuarkusUnitSecurityTest.java | 122 ++ .../unit/quarkus/SecurityTestConfig.java | 64 + .../component/accordion/AccordionTester.java | 104 ++ .../flow/component/button/ButtonTester.java | 32 + .../flow/component/charts/ChartTester.java | 319 ++++ .../checkbox/CheckboxGroupTester.java | 201 +++ .../component/checkbox/CheckboxTester.java | 59 + .../component/combobox/ComboBoxTester.java | 167 ++ .../combobox/MultiSelectComboBoxTester.java | 144 ++ .../confirmdialog/ConfirmDialogTester.java | 114 ++ .../contextmenu/ContextMenuTester.java | 428 +++++ .../datepicker/DatePickerTester.java | 76 + .../datetimepicker/DateTimePickerTester.java | 78 + .../flow/component/details/DetailsTester.java | 89 + .../flow/component/dialog/DialogTester.java | 40 + .../flow/component/grid/GridTester.java | 742 +++++++++ .../html/testbench/AnchorTester.java | 194 +++ .../html/testbench/DescriptionListTester.java | 52 + .../component/html/testbench/DivTester.java | 25 + .../html/testbench/EmphasisTester.java | 25 + .../component/html/testbench/H1Tester.java | 26 + .../component/html/testbench/H2Tester.java | 25 + .../component/html/testbench/H3Tester.java | 25 + .../component/html/testbench/H4Tester.java | 25 + .../component/html/testbench/H5Tester.java | 25 + .../component/html/testbench/H6Tester.java | 25 + .../component/html/testbench/HrTester.java | 25 + .../html/testbench/HtmlClickContainer.java | 24 + .../html/testbench/HtmlComponentTester.java | 52 + .../html/testbench/HtmlContainerTester.java | 37 + .../component/html/testbench/ImageTester.java | 25 + .../component/html/testbench/InputTester.java | 67 + .../html/testbench/ListItemTester.java | 25 + .../html/testbench/NativeButtonTester.java | 26 + .../html/testbench/NativeDetailsTester.java | 72 + .../html/testbench/NativeLabelTester.java | 25 + .../html/testbench/OrderedListTester.java | 52 + .../html/testbench/ParagraphTester.java | 25 + .../component/html/testbench/PreTester.java | 25 + .../html/testbench/RangeInputTester.java | 189 +++ .../component/html/testbench/SpanTester.java | 26 + .../html/testbench/UnorderedListTester.java | 52 + .../flow/component/listbox/ListBoxTester.java | 101 ++ .../listbox/MultiSelectListBoxTester.java | 137 ++ .../component/login/AbstractLoginTester.java | 61 + .../flow/component/login/LoginFormTester.java | 32 + .../component/login/LoginOverlayTester.java | 63 + .../flow/component/menubar/MenuBarTester.java | 333 ++++ .../messages/MessageInputTester.java | 48 + .../component/messages/MessageListTester.java | 139 ++ .../notification/NotificationTester.java | 90 ++ .../radiobutton/RadioButtonGroupTester.java | 132 ++ .../radiobutton/RadioButtonTester.java | 69 + .../routerlink/RouterLinkTester.java | 115 ++ .../flow/component/select/SelectTester.java | 94 ++ .../flow/component/sidenav/SideNavTester.java | 274 ++++ .../flow/component/tabs/TabSheetTester.java | 245 +++ .../flow/component/tabs/TabsTester.java | 188 +++ .../textfield/NumberFieldTester.java | 86 + .../component/textfield/TextAreaTester.java | 76 + .../component/textfield/TextFieldTester.java | 110 ++ .../timepicker/TimePickerTester.java | 77 + .../flow/component/upload/UploadTester.java | 446 +++++ .../virtuallist/VirtualListTester.java | 244 +++ .../vaadin/testbench/unit/BaseUIUnitTest.java | 673 ++++++++ .../com/vaadin/testbench/unit/Clickable.java | 107 ++ .../vaadin/testbench/unit/ComponentQuery.java | 669 ++++++++ .../testbench/unit/ComponentTester.java | 446 +++++ .../unit/ComponentTesterPackages.java | 36 + .../testbench/unit/ElementConditions.java | 232 +++ .../testbench/unit/LitRendererTestUtil.java | 197 +++ .../com/vaadin/testbench/unit/MetaKeys.java | 165 ++ .../vaadin/testbench/unit/MouseButton.java | 43 + .../unit/SerializationDebugUtil.java | 194 +++ .../testbench/unit/TestSignalEnvironment.java | 180 +++ .../vaadin/testbench/unit/TesterWrappers.java | 445 +++++ .../java/com/vaadin/testbench/unit/Tests.java | 38 + .../unit/UITestSpringLookupInitializer.java | 91 ++ .../unit/UIUnitTestSetupException.java | 23 + .../vaadin/testbench/unit/ViewPackages.java | 44 + .../unit/mocks/MockSpringServlet.java | 198 +++ .../unit/mocks/MockSpringServletService.java | 75 + .../unit/mocks/MockSpringVaadinSession.java | 55 + .../unit/mocks/MockWebApplicationContext.java | 325 ++++ .../SpringSecurityRequestCustomizer.java | 24 + .../vaadin/testbench/unit/component/Grid.kt | 837 ++++++++++ .../testbench/unit/internal/BasicUtils.kt | 177 ++ .../testbench/unit/internal/ComponentUtils.kt | 434 +++++ .../unit/internal/DepthFirstTreeIterator.kt | 40 + .../testbench/unit/internal/ElementUtils.kt | 126 ++ .../vaadin/testbench/unit/internal/Locator.kt | 418 +++++ .../testbench/unit/internal/MockVaadin.kt | 448 +++++ .../unit/internal/PrettyPrintTree.kt | 214 +++ .../testbench/unit/internal/Renderers.kt | 95 ++ .../vaadin/testbench/unit/internal/Routes.kt | 181 +++ .../testbench/unit/internal/Shortcuts.kt | 129 ++ .../unit/internal/TestingLifecycleHook.kt | 178 ++ .../vaadin/testbench/unit/internal/Utils.kt | 165 ++ .../testbench/unit/mocks/MockContext.kt | 288 ++++ .../unit/mocks/MockHttpEnvironment.kt | 63 + .../testbench/unit/mocks/MockHttpSession.kt | 121 ++ .../testbench/unit/mocks/MockInstantiator.kt | 75 + .../testbench/unit/mocks/MockRequest.kt | 284 ++++ .../testbench/unit/mocks/MockResponse.kt | 160 ++ .../testbench/unit/mocks/MockService.kt | 43 + .../testbench/unit/mocks/MockVaadinHelper.kt | 119 ++ .../testbench/unit/mocks/MockVaadinServlet.kt | 110 ++ .../testbench/unit/mocks/MockVaadinSession.kt | 32 + .../vaadin/testbench/unit/mocks/MockedUI.kt | 60 + .../unit/mocks/SessionAttributeMap.kt | 103 ++ .../testbench/unit/internal/AllTests.kt | 54 + 288 files changed, 33463 insertions(+) create mode 100644 browserless-test/junit6/src/main/java/com/vaadin/testbench/unit/SpringUIUnitTest.java create mode 100644 browserless-test/junit6/src/main/java/com/vaadin/testbench/unit/TreeOnFailureExtension.java create mode 100644 browserless-test/junit6/src/main/java/com/vaadin/testbench/unit/UIUnitTest.java create mode 100644 browserless-test/junit6/src/test/java/com/example/SingleParam.java create mode 100644 browserless-test/junit6/src/test/java/com/example/TemplatedParam.java create mode 100644 browserless-test/junit6/src/test/java/com/example/base/ErrorView.java create mode 100644 browserless-test/junit6/src/test/java/com/example/base/HelloWorldView.java create mode 100644 browserless-test/junit6/src/test/java/com/example/base/ParametrizedView.java create mode 100644 browserless-test/junit6/src/test/java/com/example/base/ParentView.java create mode 100644 browserless-test/junit6/src/test/java/com/example/base/WelcomeView.java create mode 100644 browserless-test/junit6/src/test/java/com/example/base/child/ChildView.java create mode 100644 browserless-test/junit6/src/test/java/com/example/base/navigation/NavigationPostponeView.java create mode 100644 browserless-test/junit6/src/test/java/com/example/base/signals/SignalsView.java create mode 100644 browserless-test/junit6/src/test/java/com/testapp/MyRouteNotFoundError.java create mode 100644 browserless-test/junit6/src/test/java/com/testapp/security/LoginView.java create mode 100644 browserless-test/junit6/src/test/java/com/testapp/security/ProtectedView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/accordion/AccordionTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/accordion/AccordionView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/button/ButtonTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/button/ButtonView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/charts/ChartTesterSeriesValueTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/charts/ChartTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/charts/ColumnChartView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/checkbox/CheckboxGroupTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/checkbox/CheckboxTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/checkbox/CheckboxView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/combobox/ComboBoxTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/combobox/ComboBoxView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/combobox/MultiSelectComboBoxTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/combobox/MultiSelectComboBoxView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/confirmdialog/ConfirmDialogTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/confirmdialog/ConfirmDialogView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/contextmenu/ContextMenuTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/contextmenu/ContextMenuView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/datepicker/DatePickerTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/datepicker/DatePickerView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/datetimepicker/DateTimePickerTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/datetimepicker/DateTimePickerView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/details/DetailsTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/details/DetailsView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/dialog/DialogTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/dialog/DialogView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/Address.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/BasicGridTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/BasicGridView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/BeanGridTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/BeanGridView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/CheckBox.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/Country.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/GetTextCellRendererTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/GridTesterSortTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/Person.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/RendererGridView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/SortGridView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/html/testbench/AnchorTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/html/testbench/AnchorView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/html/testbench/RangeInputTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/html/testbench/RangeInputView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/listbox/ListBoxTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/listbox/ListBoxView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/listbox/MultiSelectListBoxTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/login/LoginFormTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/login/LoginFormView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/login/LoginOverlayTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/login/LoginOverlayView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/menubar/MenuBarTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/menubar/MenuBarView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/messages/MessageInputTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/messages/MessageListTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/messages/MessagesView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/notification/NotificationTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/notification/NotificationView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/radiobutton/RadioButtonGroupTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/radiobutton/RadioButtonTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/radiobutton/RadioButtonView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/routerlink/AbstractTargetView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/routerlink/RouterLinkQueryParameterTargetView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/routerlink/RouterLinkRouteParameterTargetView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/routerlink/RouterLinkStaticTargetView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/routerlink/RouterLinkTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/routerlink/RouterLinkUrlParameterTargetView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/routerlink/RouterLinkView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/select/SelectTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/select/SelectView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/sidenav/SideNavTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/sidenav/SideNavView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/sidenav/TargetView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/tabs/TabSheetTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/tabs/TabSheetView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/tabs/TabsTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/tabs/TabsView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/textfield/NumberFieldTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/textfield/NumberFieldView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/textfield/TextAreaTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/textfield/TextAreaView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/textfield/TextFieldTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/textfield/TextFieldView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/timepicker/TimePickerTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/timepicker/TimePickerView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/upload/AssertingTransferProgressListener.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/upload/UploadDeprecatedAPIView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/upload/UploadTesterDeprecatedAPITest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/upload/UploadTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/upload/UploadView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/virtuallist/CallbackLitRendererVirtualListTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/virtuallist/CallbackLitRendererVirtualListView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/virtuallist/ComponentRendererVirtualListTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/virtuallist/ComponentRendererVirtualListView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/virtuallist/User.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/virtuallist/UserData.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/virtuallist/ValueProviderVirtualListTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/flow/component/virtuallist/ValueProviderVirtualListView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/testbench/dontscan/noclassdeffound/NoClassDefFoundComponent.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/testbench/dontscan/noclassdeffound/NoClassDefFoundComponentTester.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/testbench/dontscan/typenotpresent/TypeNotPresentComponent.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/testbench/dontscan/typenotpresent/TypeNotPresentComponentTester.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/testbench/unit/ClickableTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/testbench/unit/ComponentQueryTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/testbench/unit/ComponentTesterTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/testbench/unit/ElementConditionsTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/testbench/unit/NonGenericTestTester.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/testbench/unit/SecurityTestConfig.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/testbench/unit/SignalsTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/testbench/unit/SpringUIUnitBaseClassTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/testbench/unit/SpringUnitSecurityTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/testbench/unit/TestComponent.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/testbench/unit/TestComponentForConcreteTester.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/testbench/unit/TestCustomInstantiatorFactory.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/testbench/unit/TestTester.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/testbench/unit/TesterResolutionTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/testbench/unit/TesterScanTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/testbench/unit/UIUnitBaseClassTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/testbench/unit/UIUnitNavigationTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/testbench/unit/UIUnitShortcutTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/testbench/unit/viewscan/byannotatedclass/DiscoverRoutesInAnnotatedClassPackageTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/testbench/unit/viewscan/byannotatedclass/ViewPackagesTestView.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/testbench/unit/viewscan/byclasses/DiscoverRoutesInPackageByClassTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/testbench/unit/viewscan/bypackagename/DiscoverRoutesInPackageByNameTest.java create mode 100644 browserless-test/junit6/src/test/java/com/vaadin/testbench/unit/viewscan/bypackagenameandclass/DiscoverRoutesInPackageByClassAndNameTest.java create mode 100644 browserless-test/junit6/src/test/kotlin/com/vaadin/testbench/unit/TestInitListener.kt create mode 100644 browserless-test/junit6/src/test/kotlin/com/vaadin/testbench/unit/TestUtils.kt create mode 100644 browserless-test/junit6/src/test/kotlin/com/vaadin/testbench/unit/internal/AllTests.kt create mode 100644 browserless-test/junit6/src/test/kotlin/com/vaadin/testbench/unit/internal/AsyncTest.kt create mode 100644 browserless-test/junit6/src/test/kotlin/com/vaadin/testbench/unit/internal/AttachedTextField.kt create mode 100644 browserless-test/junit6/src/test/kotlin/com/vaadin/testbench/unit/internal/BasicUtilsTest.kt create mode 100644 browserless-test/junit6/src/test/kotlin/com/vaadin/testbench/unit/internal/ComponentUtilsTest.kt create mode 100644 browserless-test/junit6/src/test/kotlin/com/vaadin/testbench/unit/internal/CompositeTest.kt create mode 100644 browserless-test/junit6/src/test/kotlin/com/vaadin/testbench/unit/internal/DepthFirstTreeIteratorTest.kt create mode 100644 browserless-test/junit6/src/test/kotlin/com/vaadin/testbench/unit/internal/ElementUtilsTest.kt create mode 100644 browserless-test/junit6/src/test/kotlin/com/vaadin/testbench/unit/internal/LocatorTest.kt create mode 100644 browserless-test/junit6/src/test/kotlin/com/vaadin/testbench/unit/internal/MockVaadinTest.kt create mode 100644 browserless-test/junit6/src/test/kotlin/com/vaadin/testbench/unit/internal/PrettyPrintTreeTest.kt create mode 100644 browserless-test/junit6/src/test/kotlin/com/vaadin/testbench/unit/internal/RoutesTest.kt create mode 100644 browserless-test/junit6/src/test/kotlin/com/vaadin/testbench/unit/internal/SearchSpecTest.kt create mode 100644 browserless-test/junit6/src/test/kotlin/com/vaadin/testbench/unit/internal/ShortcutsTest.kt create mode 100644 browserless-test/junit6/src/test/kotlin/com/vaadin/testbench/unit/mocks/MockContextTest.kt create mode 100644 browserless-test/junit6/src/test/kotlin/com/vaadin/testbench/unit/mocks/MockHttpSessionTest.kt create mode 100644 browserless-test/junit6/src/test/kotlin/com/vaadin/testbench/unit/mocks/MockRequestTest.kt create mode 100644 browserless-test/junit6/src/test/kotlin/com/vaadin/testbench/unit/mocks/MockResponseTest.kt create mode 100644 browserless-test/junit6/src/test/kotlin/com/vaadin/testbench/unit/mocks/SessionAttributeMapTest.kt create mode 100644 browserless-test/junit6/src/test/kotlin/com/vaadin/testbench/unit/mocks/Utils.kt create mode 100644 browserless-test/junit6/src/test/resources/META-INF/services/com.vaadin.flow.server.VaadinServiceInitListener create mode 100644 browserless-test/junit6/src/test/webapp/VAADIN/themes/default/img/1.txt create mode 100644 browserless-test/quarkus/src/main/java/com/vaadin/testbench/unit/quarkus/QuarkusSecurityCustomizer.java create mode 100644 browserless-test/quarkus/src/main/java/com/vaadin/testbench/unit/quarkus/QuarkusTestLookupInitializer.java create mode 100644 browserless-test/quarkus/src/main/java/com/vaadin/testbench/unit/quarkus/QuarkusUIUnitTest.java create mode 100644 browserless-test/quarkus/src/main/java/com/vaadin/testbench/unit/quarkus/mocks/MockQuarkusServlet.java create mode 100644 browserless-test/quarkus/src/main/java/com/vaadin/testbench/unit/quarkus/mocks/MockQuarkusServletService.java create mode 100644 browserless-test/quarkus/src/test/java/com/example/base/DummyService.java create mode 100644 browserless-test/quarkus/src/test/java/com/example/base/HelloWorldView.java create mode 100644 browserless-test/quarkus/src/test/java/com/example/base/WelcomeView.java create mode 100644 browserless-test/quarkus/src/test/java/com/testapp/security/AnyRoleView.java create mode 100644 browserless-test/quarkus/src/test/java/com/testapp/security/LoginView.java create mode 100644 browserless-test/quarkus/src/test/java/com/testapp/security/ProtectedView.java create mode 100644 browserless-test/quarkus/src/test/java/com/testapp/security/RoleRestrictedView.java create mode 100644 browserless-test/quarkus/src/test/java/com/vaadin/testbench/unit/quarkus/QuarkusSecurityCustomizerTest.java create mode 100644 browserless-test/quarkus/src/test/java/com/vaadin/testbench/unit/quarkus/QuarkusUIUnitBaseClassTest.java create mode 100644 browserless-test/quarkus/src/test/java/com/vaadin/testbench/unit/quarkus/QuarkusUnitSecurityTest.java create mode 100644 browserless-test/quarkus/src/test/java/com/vaadin/testbench/unit/quarkus/SecurityTestConfig.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/accordion/AccordionTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/button/ButtonTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/charts/ChartTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/checkbox/CheckboxGroupTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/checkbox/CheckboxTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/combobox/ComboBoxTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/combobox/MultiSelectComboBoxTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/confirmdialog/ConfirmDialogTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/contextmenu/ContextMenuTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/datepicker/DatePickerTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/datetimepicker/DateTimePickerTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/details/DetailsTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/dialog/DialogTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/grid/GridTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/html/testbench/AnchorTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/html/testbench/DescriptionListTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/html/testbench/DivTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/html/testbench/EmphasisTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/html/testbench/H1Tester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/html/testbench/H2Tester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/html/testbench/H3Tester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/html/testbench/H4Tester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/html/testbench/H5Tester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/html/testbench/H6Tester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/html/testbench/HrTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/html/testbench/HtmlClickContainer.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/html/testbench/HtmlComponentTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/html/testbench/HtmlContainerTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/html/testbench/ImageTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/html/testbench/InputTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/html/testbench/ListItemTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/html/testbench/NativeButtonTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/html/testbench/NativeDetailsTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/html/testbench/NativeLabelTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/html/testbench/OrderedListTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/html/testbench/ParagraphTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/html/testbench/PreTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/html/testbench/RangeInputTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/html/testbench/SpanTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/html/testbench/UnorderedListTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/listbox/ListBoxTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/listbox/MultiSelectListBoxTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/login/AbstractLoginTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/login/LoginFormTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/login/LoginOverlayTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/menubar/MenuBarTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/messages/MessageInputTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/messages/MessageListTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/notification/NotificationTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/radiobutton/RadioButtonGroupTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/radiobutton/RadioButtonTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/routerlink/RouterLinkTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/select/SelectTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/sidenav/SideNavTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/tabs/TabSheetTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/tabs/TabsTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/textfield/NumberFieldTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/textfield/TextAreaTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/textfield/TextFieldTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/timepicker/TimePickerTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/upload/UploadTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/flow/component/virtuallist/VirtualListTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/testbench/unit/BaseUIUnitTest.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/testbench/unit/Clickable.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/testbench/unit/ComponentQuery.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/testbench/unit/ComponentTester.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/testbench/unit/ComponentTesterPackages.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/testbench/unit/ElementConditions.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/testbench/unit/LitRendererTestUtil.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/testbench/unit/MetaKeys.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/testbench/unit/MouseButton.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/testbench/unit/SerializationDebugUtil.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/testbench/unit/TestSignalEnvironment.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/testbench/unit/TesterWrappers.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/testbench/unit/Tests.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/testbench/unit/UITestSpringLookupInitializer.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/testbench/unit/UIUnitTestSetupException.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/testbench/unit/ViewPackages.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/testbench/unit/mocks/MockSpringServlet.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/testbench/unit/mocks/MockSpringServletService.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/testbench/unit/mocks/MockSpringVaadinSession.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/testbench/unit/mocks/MockWebApplicationContext.java create mode 100644 browserless-test/shared/src/main/java/com/vaadin/testbench/unit/mocks/SpringSecurityRequestCustomizer.java create mode 100644 browserless-test/shared/src/main/kotlin/com/vaadin/testbench/unit/component/Grid.kt create mode 100644 browserless-test/shared/src/main/kotlin/com/vaadin/testbench/unit/internal/BasicUtils.kt create mode 100644 browserless-test/shared/src/main/kotlin/com/vaadin/testbench/unit/internal/ComponentUtils.kt create mode 100644 browserless-test/shared/src/main/kotlin/com/vaadin/testbench/unit/internal/DepthFirstTreeIterator.kt create mode 100644 browserless-test/shared/src/main/kotlin/com/vaadin/testbench/unit/internal/ElementUtils.kt create mode 100644 browserless-test/shared/src/main/kotlin/com/vaadin/testbench/unit/internal/Locator.kt create mode 100644 browserless-test/shared/src/main/kotlin/com/vaadin/testbench/unit/internal/MockVaadin.kt create mode 100644 browserless-test/shared/src/main/kotlin/com/vaadin/testbench/unit/internal/PrettyPrintTree.kt create mode 100644 browserless-test/shared/src/main/kotlin/com/vaadin/testbench/unit/internal/Renderers.kt create mode 100644 browserless-test/shared/src/main/kotlin/com/vaadin/testbench/unit/internal/Routes.kt create mode 100644 browserless-test/shared/src/main/kotlin/com/vaadin/testbench/unit/internal/Shortcuts.kt create mode 100644 browserless-test/shared/src/main/kotlin/com/vaadin/testbench/unit/internal/TestingLifecycleHook.kt create mode 100644 browserless-test/shared/src/main/kotlin/com/vaadin/testbench/unit/internal/Utils.kt create mode 100644 browserless-test/shared/src/main/kotlin/com/vaadin/testbench/unit/mocks/MockContext.kt create mode 100644 browserless-test/shared/src/main/kotlin/com/vaadin/testbench/unit/mocks/MockHttpEnvironment.kt create mode 100644 browserless-test/shared/src/main/kotlin/com/vaadin/testbench/unit/mocks/MockHttpSession.kt create mode 100644 browserless-test/shared/src/main/kotlin/com/vaadin/testbench/unit/mocks/MockInstantiator.kt create mode 100644 browserless-test/shared/src/main/kotlin/com/vaadin/testbench/unit/mocks/MockRequest.kt create mode 100644 browserless-test/shared/src/main/kotlin/com/vaadin/testbench/unit/mocks/MockResponse.kt create mode 100644 browserless-test/shared/src/main/kotlin/com/vaadin/testbench/unit/mocks/MockService.kt create mode 100644 browserless-test/shared/src/main/kotlin/com/vaadin/testbench/unit/mocks/MockVaadinHelper.kt create mode 100644 browserless-test/shared/src/main/kotlin/com/vaadin/testbench/unit/mocks/MockVaadinServlet.kt create mode 100644 browserless-test/shared/src/main/kotlin/com/vaadin/testbench/unit/mocks/MockVaadinSession.kt create mode 100644 browserless-test/shared/src/main/kotlin/com/vaadin/testbench/unit/mocks/MockedUI.kt create mode 100644 browserless-test/shared/src/main/kotlin/com/vaadin/testbench/unit/mocks/SessionAttributeMap.kt create mode 100644 browserless-test/shared/src/test/kotlin/com/vaadin/testbench/unit/internal/AllTests.kt diff --git a/browserless-test/junit6/src/main/java/com/vaadin/testbench/unit/SpringUIUnitTest.java b/browserless-test/junit6/src/main/java/com/vaadin/testbench/unit/SpringUIUnitTest.java new file mode 100644 index 000000000..78da0f62a --- /dev/null +++ b/browserless-test/junit6/src/main/java/com/vaadin/testbench/unit/SpringUIUnitTest.java @@ -0,0 +1,76 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.testbench.unit; + +import java.util.Set; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.test.context.TestExecutionListeners; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import com.vaadin.testbench.unit.internal.MockVaadin; +import com.vaadin.testbench.unit.mocks.MockSpringServlet; +import com.vaadin.testbench.unit.mocks.MockedUI; +import com.vaadin.testbench.unit.mocks.SpringSecurityRequestCustomizer; + +/** + * Base JUnit 5+ class for UI unit testing applications based on Spring + * Framework. + * + * This class provides functionality of the Spring TestContext Framework, in + * addition to set up a mock Vaadin Spring environment, so that views and + * components built upon dependency injection and AOP can be correctly be + * handled during unit testing. + * + * Usually when unit testing a UI view it is not needed to bootstrap the whole + * application. Subclasses can therefore be annotated + * with @{@link org.springframework.test.context.ContextConfiguration} or other + * Spring Testing annotations to load only required component or to provide mock + * services implementations. + * + *
+ * {@code
+ * @ContextConfiguration(classes = ViewTestConfig.class)
+ * class ViewTest extends SpringUIUnitTest {
+ *
+ * }
+ * @Configuration
+ * class ViewTestConfig {
+ *     @Bean
+ *     MyService myService() {
+ *         return new my MockMyService();
+ *     }
+ * }
+ * }
+ * 
+ */ +@ExtendWith({ SpringExtension.class }) +@TestExecutionListeners(listeners = UITestSpringLookupInitializer.class, mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS) +public abstract class SpringUIUnitTest extends UIUnitTest { + + @Autowired + private ApplicationContext applicationContext; + + @Override + protected Set> lookupServices() { + return Set.of(UITestSpringLookupInitializer.class, + SpringSecurityRequestCustomizer.class); + } + + @BeforeEach + protected void initVaadinEnvironment() { + scanTesters(); + MockSpringServlet servlet = new MockSpringServlet(discoverRoutes(), + applicationContext, MockedUI::new); + MockVaadin.setup(MockedUI::new, servlet, lookupServices()); + } +} diff --git a/browserless-test/junit6/src/main/java/com/vaadin/testbench/unit/TreeOnFailureExtension.java b/browserless-test/junit6/src/main/java/com/vaadin/testbench/unit/TreeOnFailureExtension.java new file mode 100644 index 000000000..51d87b770 --- /dev/null +++ b/browserless-test/junit6/src/main/java/com/vaadin/testbench/unit/TreeOnFailureExtension.java @@ -0,0 +1,39 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.testbench.unit; + +import org.junit.jupiter.api.extension.AfterTestExecutionCallback; +import org.junit.jupiter.api.extension.ExtensionContext; + +import com.vaadin.flow.component.UI; +import com.vaadin.testbench.unit.internal.PrettyPrintTree; + +/** + * JUnit5+ extension that will collect and output the component tree for the + * failing test UI. + *

+ * This can help with identifying a problem that has happened in the test where + * a component is missing or has faulty data. + */ +public class TreeOnFailureExtension implements AfterTestExecutionCallback { + + @Override + public void afterTestExecution(ExtensionContext extensionContext) { + boolean testFailed = extensionContext.getExecutionException() + .isPresent(); + if (testFailed) { + final String prettyPrintTree = PrettyPrintTree.Companion + .ofVaadin(UI.getCurrent()).print(); + extensionContext.publishReportEntry("Test " + + extensionContext.getTestClass().get().getSimpleName() + + "::" + extensionContext.getTestMethod().get().getName() + + " failed with the tree:\n" + prettyPrintTree); + } + } +} diff --git a/browserless-test/junit6/src/main/java/com/vaadin/testbench/unit/UIUnitTest.java b/browserless-test/junit6/src/main/java/com/vaadin/testbench/unit/UIUnitTest.java new file mode 100644 index 000000000..a5f311cfb --- /dev/null +++ b/browserless-test/junit6/src/main/java/com/vaadin/testbench/unit/UIUnitTest.java @@ -0,0 +1,91 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.testbench.unit; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; + +/** + * Base JUnit 5+ class for UI unit tests. + * + * The class automatically scans classpath for routes and error views. + * Subclasses should typically restrict classpath scanning to a specific + * packages for faster bootstrap, by using {@link ViewPackages} annotation. If + * the annotation is not present a full classpath scan is performed + * + *

+ * {@code
+ * @ViewPackages(classes = {CartView.class, CheckoutView.class})
+ * class CartViewTest extends UIUnitTest {
+ * }
+ *
+ * @ViewPackages(packages = {"com.example.shop.cart", "com.example.security"})
+ * class CartViewTest extends UIUnitTest {
+ * }
+ *
+ * @ViewPackages(
+ *    classes = {CartView.class, CheckoutView.class},
+ *    packages = {"com.example.security"}
+ * )
+ * class CartViewTest extends UIUnitTest {
+ * }
+ * 
+ * + * Set up of Vaadin environment is performed before each test by {@link + * #initVaadinEnvironment()} method, and will be executed before + * {@code @BeforeEach} methods defined in subclasses. At the same way, cleanup + * tasks operated by {@link #cleanVaadinEnvironment()} are executed after each + * test, and after all {@code @AfterEach} annotated methods in subclasses. + * + * Usually, it is not necessary to override {@link #initVaadinEnvironment()} or + * {@link #cleanVaadinEnvironment()} methods, but if this is done it is + * mandatory to add the {@code @BeforeEach} and {@code @AfterEach} annotations + * in the subclass, in order to have hooks handled by testing framework. + * + * A use case for overriding {@link #initVaadinEnvironment()} is to provide + * custom Flow service implementations supported by {@link + * com.vaadin.flow.di.Lookup} SPI. Implementations can be provided overriding + * {@link #initVaadinEnvironment()} and passing to super implementation the + * service classes that should be initialized during setup. + * + *
+ * {@code
+ * @BeforeEach
+ * @Override
+ * void initVaadinEnvironment() {
+ *     super.initVaadinEnvironment(CustomInstantiatorFactory.class);
+ * }
+ * }
+ * 
+ *

+ * To get a graphical ascii representation of the UI tree on failure add the + * annotation {@code @ExtendWith(TreeOnFailureExtension.class)} to the test + * class. + * + * @see ViewPackages + */ +public abstract class UIUnitTest extends BaseUIUnitTest + implements TesterWrappers { + + @BeforeEach + protected void initVaadinEnvironment() { + super.initVaadinEnvironment(); + } + + @AfterEach + @Override + protected void cleanVaadinEnvironment() { + super.cleanVaadinEnvironment(); + } + + @Override + protected final String testingEngine() { + return "JUnit 5+"; + } +} diff --git a/browserless-test/junit6/src/test/java/com/example/SingleParam.java b/browserless-test/junit6/src/test/java/com/example/SingleParam.java new file mode 100644 index 000000000..3c2b38a58 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/example/SingleParam.java @@ -0,0 +1,26 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.example; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.router.BeforeEvent; +import com.vaadin.flow.router.HasUrlParameter; +import com.vaadin.flow.router.Route; + +@Route("param") +@Tag("div") +public class SingleParam extends Component implements HasUrlParameter { + public String parameter; + + @Override + public void setParameter(BeforeEvent event, String parameter) { + this.parameter = parameter; + } +} diff --git a/browserless-test/junit6/src/test/java/com/example/TemplatedParam.java b/browserless-test/junit6/src/test/java/com/example/TemplatedParam.java new file mode 100644 index 000000000..960c15324 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/example/TemplatedParam.java @@ -0,0 +1,26 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.example; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.router.BeforeEnterEvent; +import com.vaadin.flow.router.BeforeEnterObserver; +import com.vaadin.flow.router.Route; + +@Route("template/:param") +@Tag("div") +public class TemplatedParam extends Component implements BeforeEnterObserver { + public String parameter; + + @Override + public void beforeEnter(BeforeEnterEvent event) { + parameter = event.getRouteParameters().get("param").get(); + } +} diff --git a/browserless-test/junit6/src/test/java/com/example/base/ErrorView.java b/browserless-test/junit6/src/test/java/com/example/base/ErrorView.java new file mode 100644 index 000000000..b773f1d71 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/example/base/ErrorView.java @@ -0,0 +1,27 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.example.base; + +import com.vaadin.flow.component.orderedlayout.VerticalLayout; +import com.vaadin.flow.router.BeforeEnterEvent; +import com.vaadin.flow.router.ErrorParameter; +import com.vaadin.flow.router.HasErrorParameter; +import com.vaadin.flow.router.NotFoundException; + +public class ErrorView extends VerticalLayout + implements HasErrorParameter { + @Override + public int setErrorParameter(BeforeEnterEvent event, + ErrorParameter parameter) { + if (parameter.getException() instanceof NotFoundException) { + throw (NotFoundException) parameter.getException(); + } + throw new RuntimeException(parameter.getCaughtException()); + } +} diff --git a/browserless-test/junit6/src/test/java/com/example/base/HelloWorldView.java b/browserless-test/junit6/src/test/java/com/example/base/HelloWorldView.java new file mode 100644 index 000000000..2c67027f2 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/example/base/HelloWorldView.java @@ -0,0 +1,20 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.example.base; + +import com.vaadin.flow.component.button.Button; +import com.vaadin.flow.component.orderedlayout.VerticalLayout; +import com.vaadin.flow.router.Route; + +@Route("helloworld") +public class HelloWorldView extends VerticalLayout { + public HelloWorldView() { + add(new Button("Hello, World!")); + } +} diff --git a/browserless-test/junit6/src/test/java/com/example/base/ParametrizedView.java b/browserless-test/junit6/src/test/java/com/example/base/ParametrizedView.java new file mode 100644 index 000000000..ec80cea06 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/example/base/ParametrizedView.java @@ -0,0 +1,29 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.example.base; + +import com.vaadin.flow.component.orderedlayout.VerticalLayout; +import com.vaadin.flow.router.BeforeEvent; +import com.vaadin.flow.router.HasUrlParameter; +import com.vaadin.flow.router.QueryParameters; +import com.vaadin.flow.router.Route; + +@Route("params") +public class ParametrizedView extends VerticalLayout + implements HasUrlParameter { + + Integer parameter; + QueryParameters qp; + + @Override + public void setParameter(BeforeEvent event, Integer parameter) { + this.parameter = parameter; + qp = event.getLocation().getQueryParameters(); + } +} diff --git a/browserless-test/junit6/src/test/java/com/example/base/ParentView.java b/browserless-test/junit6/src/test/java/com/example/base/ParentView.java new file mode 100644 index 000000000..deba32350 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/example/base/ParentView.java @@ -0,0 +1,17 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.example.base; + +import com.vaadin.flow.component.orderedlayout.VerticalLayout; +import com.vaadin.flow.router.RoutePrefix; +import com.vaadin.flow.router.RouterLayout; + +@RoutePrefix("parent") +public class ParentView extends VerticalLayout implements RouterLayout { +} diff --git a/browserless-test/junit6/src/test/java/com/example/base/WelcomeView.java b/browserless-test/junit6/src/test/java/com/example/base/WelcomeView.java new file mode 100644 index 000000000..af2f869e1 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/example/base/WelcomeView.java @@ -0,0 +1,25 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.example.base; + +import com.vaadin.flow.component.Text; +import com.vaadin.flow.component.orderedlayout.VerticalLayout; +import com.vaadin.flow.router.Route; +import com.vaadin.flow.router.RouteAlias; +import com.vaadin.flow.server.PWA; + +@Route("welcome") +@RouteAlias("") +@PWA(name = "My Foo PWA", shortName = "Foo PWA") +public class WelcomeView extends VerticalLayout { + public WelcomeView() { + setWidth(null); + add(new Text("Welcome!")); + } +} diff --git a/browserless-test/junit6/src/test/java/com/example/base/child/ChildView.java b/browserless-test/junit6/src/test/java/com/example/base/child/ChildView.java new file mode 100644 index 000000000..d278bb44e --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/example/base/child/ChildView.java @@ -0,0 +1,19 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.example.base.child; + +import com.example.base.ParentView; + +import com.vaadin.flow.component.orderedlayout.VerticalLayout; +import com.vaadin.flow.router.Route; + +@Route(value = "child", layout = ParentView.class) +public class ChildView extends VerticalLayout { + +} diff --git a/browserless-test/junit6/src/test/java/com/example/base/navigation/NavigationPostponeView.java b/browserless-test/junit6/src/test/java/com/example/base/navigation/NavigationPostponeView.java new file mode 100644 index 000000000..9e770c660 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/example/base/navigation/NavigationPostponeView.java @@ -0,0 +1,34 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.example.base.navigation; + +import com.vaadin.flow.component.button.Button; +import com.vaadin.flow.component.dialog.Dialog; +import com.vaadin.flow.component.html.Span; +import com.vaadin.flow.component.orderedlayout.VerticalLayout; +import com.vaadin.flow.router.BeforeLeaveEvent; +import com.vaadin.flow.router.BeforeLeaveObserver; +import com.vaadin.flow.router.Route; + +@Route("navigation-postpone") +public class NavigationPostponeView extends VerticalLayout + implements BeforeLeaveObserver { + @Override + public void beforeLeave(BeforeLeaveEvent event) { + BeforeLeaveEvent.ContinueNavigationAction action = event.postpone(); + Dialog dialog = new Dialog(); + dialog.add(new Span( + "Are you sure you want to leave such a beautiful view?"), + new Button("Yes", ev -> { + action.proceed(); + dialog.close(); + }), new Button("No", ev -> dialog.close())); + dialog.open(); + } +} diff --git a/browserless-test/junit6/src/test/java/com/example/base/signals/SignalsView.java b/browserless-test/junit6/src/test/java/com/example/base/signals/SignalsView.java new file mode 100644 index 000000000..5a21dd5bd --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/example/base/signals/SignalsView.java @@ -0,0 +1,79 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.example.base.signals; + +import java.util.concurrent.CompletableFuture; + +import com.vaadin.flow.component.html.Div; +import com.vaadin.flow.component.html.NativeButton; +import com.vaadin.flow.component.html.Span; +import com.vaadin.flow.router.Route; +import com.vaadin.flow.signals.Signal; +import com.vaadin.flow.signals.shared.SharedNumberSignal; + +@Route(value = "signals") +public class SignalsView extends Div { + + public final NativeButton incrementButton; + public final NativeButton quickBackgroundTaskButton; + public final NativeButton slowBackgroundTaskButton; + public final Span counter; + public final Span asyncCounter; + public final Span asyncWithDelayCounter; + public final SharedNumberSignal numberSignal; + public final SharedNumberSignal asyncNumberSignal; + public final SharedNumberSignal asyncWithDelayNumberSignal; + + public SignalsView() { + numberSignal = new SharedNumberSignal(); + + Signal computedSignal = numberSignal + .mapIntValue(counter -> "Counter: " + counter); + incrementButton = new NativeButton("Increment", + ev -> numberSignal.incrementBy(1.0)); + counter = new Span("Counter: -"); + counter.bindText(computedSignal); + + asyncNumberSignal = new SharedNumberSignal(); + Signal asyncComputedSignal = asyncNumberSignal + .mapIntValue(counter -> "Counter: " + counter); + asyncCounter = new Span("Counter: -"); + asyncCounter.bindText(asyncComputedSignal); + + asyncWithDelayNumberSignal = new SharedNumberSignal(); + asyncWithDelayCounter = new Span("Counter: -"); + + quickBackgroundTaskButton = new NativeButton("Quick background task", + event -> { + CompletableFuture.runAsync( + () -> asyncNumberSignal.incrementBy(10.0), + CompletableFuture.delayedExecutor(100, + java.util.concurrent.TimeUnit.MILLISECONDS)); + }); + + Signal.effect(asyncWithDelayCounter, () -> { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException(e); + } + asyncWithDelayCounter.setText("Counter: " + + asyncWithDelayNumberSignal.getAsInt() + " (delayed)"); + }); + slowBackgroundTaskButton = new NativeButton("Quick background task", + event -> CompletableFuture.runAsync(() -> { + asyncWithDelayNumberSignal.incrementBy(10.0); + })); + + add(incrementButton, quickBackgroundTaskButton, + slowBackgroundTaskButton, counter, asyncCounter, + asyncWithDelayCounter); + } +} diff --git a/browserless-test/junit6/src/test/java/com/testapp/MyRouteNotFoundError.java b/browserless-test/junit6/src/test/java/com/testapp/MyRouteNotFoundError.java new file mode 100644 index 000000000..0e9223026 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/testapp/MyRouteNotFoundError.java @@ -0,0 +1,28 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.testapp; + +import com.vaadin.flow.component.orderedlayout.VerticalLayout; +import com.vaadin.flow.router.BeforeEnterEvent; +import com.vaadin.flow.router.ErrorParameter; +import com.vaadin.flow.router.HasErrorParameter; +import com.vaadin.flow.router.NotFoundException; + +/** + * Having an app-custom [NotFoundException] handler should not crash the mocking + * process: https://github.com/mvysny/karibu-testing/issues/50 + */ +public class MyRouteNotFoundError extends VerticalLayout + implements HasErrorParameter { + @Override + public int setErrorParameter(BeforeEnterEvent event, + ErrorParameter parameter) { + throw new RuntimeException(parameter.getException()); + } +} diff --git a/browserless-test/junit6/src/test/java/com/testapp/security/LoginView.java b/browserless-test/junit6/src/test/java/com/testapp/security/LoginView.java new file mode 100644 index 000000000..b056f03e2 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/testapp/security/LoginView.java @@ -0,0 +1,19 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.testapp.security; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.HasComponents; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.router.Route; + +@Tag("div") +@Route(value = "login", registerAtStartup = false) +public class LoginView extends Component implements HasComponents { +} diff --git a/browserless-test/junit6/src/test/java/com/testapp/security/ProtectedView.java b/browserless-test/junit6/src/test/java/com/testapp/security/ProtectedView.java new file mode 100644 index 000000000..45124b511 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/testapp/security/ProtectedView.java @@ -0,0 +1,22 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.testapp.security; + +import jakarta.annotation.security.PermitAll; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.HasComponents; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.router.Route; + +@Tag("div") +@Route(value = "", registerAtStartup = false) +@PermitAll +public class ProtectedView extends Component implements HasComponents { +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/accordion/AccordionTesterTest.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/accordion/AccordionTesterTest.java new file mode 100644 index 000000000..3be5b3d11 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/accordion/AccordionTesterTest.java @@ -0,0 +1,68 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.accordion; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.testbench.unit.UIUnitTest; +import com.vaadin.testbench.unit.ViewPackages; + +@ViewPackages +class AccordionTesterTest extends UIUnitTest { + + AccordionView view; + + @BeforeEach + public void registerView() { + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(AccordionView.class); + view = navigate(AccordionView.class); + } + + @Test + void getPanelBySummary_returnsCorrectPanel() { + final AccordionTester wrap = test(view.accordion); + wrap.openDetails("Red"); + Assertions.assertSame(view.redPanel, wrap.getPanel("Red")); + wrap.openDetails("Disabled"); + Assertions.assertSame(view.disabledPanel, wrap.getPanel("Disabled")); + } + + @Test + void closedPanel_getPanelThrows() { + Assertions.assertThrows(IllegalStateException.class, + () -> test(view.accordion).getPanel("Green")); + } + + @Test + void isOpen_seesCorrectPanel() { + view.accordion.open(view.redPanel); + + final AccordionTester wrap = test(view.accordion); + Assertions.assertTrue(wrap.isOpen("Red"), "Red should be open"); + Assertions.assertFalse(wrap.isOpen("Green"), "Only red should be open"); + + view.accordion.open(view.greenPanel); + + Assertions.assertFalse(wrap.isOpen("Red"), + "Red should close after green is open"); + } + + @Test + void hasPanel_returnsTrueForExistingPanel() { + final AccordionTester wrap = test(view.accordion); + Assertions.assertTrue(wrap.hasPanel("Green"), + "Green panel should exist"); + Assertions.assertFalse(wrap.hasPanel("Orange"), + "No Orange panel is added"); + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/accordion/AccordionView.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/accordion/AccordionView.java new file mode 100644 index 000000000..457655df4 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/accordion/AccordionView.java @@ -0,0 +1,43 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.accordion; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.HasComponents; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.component.html.Div; +import com.vaadin.flow.component.html.Span; +import com.vaadin.flow.router.Route; + +@Tag("div") +@Route(value = "accordion", registerAtStartup = false) +public class AccordionView extends Component implements HasComponents { + + Accordion accordion; + + // for content testing + Div redDiv, greenDiv; + AccordionPanel redPanel, greenPanel, disabledPanel; + + public AccordionView() { + + accordion = new Accordion(); + + redDiv = new Div(); + + greenDiv = new Div(); + + redPanel = accordion.add("Red", redDiv); + greenPanel = accordion.add("Green", greenDiv); + disabledPanel = accordion.add("Disabled", new Span("Disabled panel")); + disabledPanel.setEnabled(false); + + add(accordion); + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/button/ButtonTesterTest.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/button/ButtonTesterTest.java new file mode 100644 index 000000000..697a537a9 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/button/ButtonTesterTest.java @@ -0,0 +1,131 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.button; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.vaadin.flow.component.ClickEvent; +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.testbench.unit.MetaKeys; +import com.vaadin.testbench.unit.UIUnitTest; +import com.vaadin.testbench.unit.ViewPackages; + +@ViewPackages +public class ButtonTesterTest extends UIUnitTest { + + private ButtonView view; + + @BeforeEach + public void registerView() { + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(ButtonView.class); + view = navigate(ButtonView.class); + } + + @Test + public void buttonWithDisableOnClick_notUsableAfterClick() { + view.button.setDisableOnClick(true); + + final ButtonTester button_ = test(ButtonTester.class, view.button); + + Assertions.assertTrue(button_.isUsable(), + "Button should be usable before click"); + + button_.click(); + + Assertions.assertFalse(button_.isUsable(), + "Button should have been disabled after click"); + + Assertions.assertThrows(IllegalStateException.class, + () -> button_.click(), + "Illegal state should be thrown for disabled button"); + } + + @Test + public void clickWithMiddleButton_middleButtonClickShouldBeRegistered() { + AtomicInteger mouseButton = new AtomicInteger(-1); + view.button + .addClickListener(event -> mouseButton.set(event.getButton())); + + final ButtonTester button_ = test(ButtonTester.class, view.button); + button_.middleClick(); + + Assertions.assertEquals(1, mouseButton.get(), + "Click event should have sent with middle click"); + } + + @Test + public void clickWithRightButton_rightButtonClickShouldBeRegistered() { + AtomicInteger mouseButton = new AtomicInteger(-1); + view.button + .addClickListener(event -> mouseButton.set(event.getButton())); + + final ButtonTester button_ = test(ButtonTester.class, view.button); + button_.rightClick(); + + Assertions.assertEquals(2, mouseButton.get(), + "Click event should have sent with right click"); + } + + @Test + public void normalClick_noMetaKeysMarkedAsUsed() { + AtomicReference event = new AtomicReference<>(null); + view.button.addClickListener( + clickEvent -> event.compareAndSet(null, clickEvent)); + + final ButtonTester button_ = test(ButtonTester.class, view.button); + button_.click(); + + Assertions.assertNotNull(event.get(), + "event should have fired and recorded"); + Assertions.assertFalse(event.get().isCtrlKey(), + "Ctrl should not have been used"); + Assertions.assertFalse(event.get().isShiftKey(), + "Shift should not have been used"); + Assertions.assertFalse(event.get().isAltKey(), + "Alt should not have been used"); + Assertions.assertFalse(event.get().isMetaKey(), + "Meta should not have been used"); + } + + @Test + public void clickWithMeta_metaKeysMarkedAsUsed() { + AtomicReference event = new AtomicReference<>(null); + view.button.addClickListener(clickEvent -> event.set(clickEvent)); + + final ButtonTester button_ = test(ButtonTester.class, view.button); + button_.click(new MetaKeys(true, true, true, true)); + + Assertions.assertNotNull(event.get(), + "event should have fired and recorded"); + Assertions.assertTrue(event.get().isCtrlKey(), + "Ctrl should have been used"); + Assertions.assertTrue(event.get().isShiftKey(), + "Shift should have been used"); + Assertions.assertTrue(event.get().isAltKey(), + "Alt should have been used"); + Assertions.assertTrue(event.get().isMetaKey(), + "Meta should have been used"); + + button_.click(new MetaKeys(true, true, false, false)); + Assertions.assertTrue(event.get().isCtrlKey(), + "Ctrl should have been used"); + Assertions.assertTrue(event.get().isShiftKey(), + "Shift should have been used"); + Assertions.assertFalse(event.get().isAltKey(), + "Alt should not have been used"); + Assertions.assertFalse(event.get().isMetaKey(), + "Meta should not have been used"); + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/button/ButtonView.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/button/ButtonView.java new file mode 100644 index 000000000..f70209deb --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/button/ButtonView.java @@ -0,0 +1,25 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.button; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.HasComponents; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.router.Route; + +@Tag("div") +@Route(value = "button", registerAtStartup = false) +public class ButtonView extends Component implements HasComponents { + Button button; + + public ButtonView() { + button = new Button(); + add(button); + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/charts/ChartTesterSeriesValueTest.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/charts/ChartTesterSeriesValueTest.java new file mode 100644 index 000000000..751581aa0 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/charts/ChartTesterSeriesValueTest.java @@ -0,0 +1,344 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.charts; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import com.vaadin.flow.component.charts.model.Configuration; +import com.vaadin.flow.component.charts.model.DataProviderSeries; +import com.vaadin.flow.component.charts.model.DataSeries; +import com.vaadin.flow.component.charts.model.DataSeriesItem; +import com.vaadin.flow.component.charts.model.HeatSeries; +import com.vaadin.flow.component.charts.model.ListSeries; +import com.vaadin.flow.component.charts.model.TreeSeries; +import com.vaadin.flow.component.charts.model.TreeSeriesItem; +import com.vaadin.flow.data.provider.DataProvider; +import com.vaadin.flow.data.provider.ListDataProvider; + +class ChartTesterSeriesValueTest { + + private ChartTester tester = new ChartTester<>(new Chart()); + + @Test + void getValuesFromSeries_listSeries_getsValues() { + ListSeries series = setupListSeries(); + Assertions.assertIterableEquals(tester.getValuesFromSeries(series), + List.of(series.getData())); + } + + @Test + void getPointValue_listSeries_getsCorrectValue() { + ListSeries series = setupListSeries(); + Assertions.assertEquals(series.getData()[0], + tester.getPointValueFromSeries(series, "Jan")); + Assertions.assertEquals(series.getData()[1], + tester.getPointValueFromSeries(series, "Feb")); + Assertions.assertEquals(series.getData()[2], + tester.getPointValueFromSeries(series, "Mar")); + Assertions.assertEquals(series.getData()[3], + tester.getPointValueFromSeries(series, "Apr")); + Assertions.assertEquals(series.getData()[4], + tester.getPointValueFromSeries(series, "May")); + Assertions.assertEquals(series.getData()[5], + tester.getPointValueFromSeries(series, "Jun")); + Assertions.assertEquals(series.getData()[6], + tester.getPointValueFromSeries(series, "Jul")); + Assertions.assertEquals(series.getData()[7], + tester.getPointValueFromSeries(series, "Aug")); + Assertions.assertEquals(series.getData()[8], + tester.getPointValueFromSeries(series, "Sep")); + Assertions.assertEquals(series.getData()[9], + tester.getPointValueFromSeries(series, "Oct")); + Assertions.assertEquals(series.getData()[10], + tester.getPointValueFromSeries(series, "Nov")); + Assertions.assertEquals(series.getData()[11], + tester.getPointValueFromSeries(series, "Dec")); + } + + @Test + void getPointValue_listSeries_invalidCategoryThrows() { + ListSeries series = setupListSeries(); + IllegalArgumentException exception = Assertions.assertThrows( + IllegalArgumentException.class, + () -> tester.getPointValueFromSeries(series, "XYZ")); + Assertions.assertTrue(exception.getMessage().contains("XYZ")); + Assertions.assertTrue( + exception.getMessage().contains("Invalid X-Axis category")); + } + + @Test + void getPointValue_listSeries_missingXAxisCategoriesThrows() { + ListSeries series = new ListSeries("MyTest"); + IllegalStateException exception = Assertions.assertThrows( + IllegalStateException.class, + () -> tester.getPointValueFromSeries(series, "XYZ")); + Assertions.assertTrue(exception.getMessage() + .contains("X-Axis categories not configured")); + } + + @Test + void getValuesFromSeries_dataSeries_getsValues() { + DataSeries series = createDataSeries(); + + Assertions.assertIterableEquals(tester.getValuesFromSeries(series), + series.getData().stream().map(DataSeriesItem::getY) + .collect(Collectors.toList())); + } + + @Test + void getPointValue_dataSeries_getsCorrectValue() { + DataSeries series = createDataSeries(); + for (DataSeriesItem item : series.getData()) { + Assertions.assertEquals(item.getY(), + tester.getPointValueFromSeries(series, item.getName())); + } + } + + @Test + void getPointValue_dataSeries_notExistingThrows() { + DataSeries series = createDataSeries(); + IllegalArgumentException exception = Assertions.assertThrows( + IllegalArgumentException.class, + () -> tester.getPointValueFromSeries(series, "XYZ")); + Assertions.assertTrue(exception.getMessage().contains("XYZ")); + Assertions.assertTrue( + exception.getMessage().contains("Invalid DataSeriesItem name")); + } + + @Test + void getValuesFromSeries_treeSeries_unsupported() { + TreeSeries series = setupTreeSeries(); + + UnsupportedOperationException exception = Assertions.assertThrows( + UnsupportedOperationException.class, + () -> tester.getValuesFromSeries(series)); + Assertions.assertTrue(exception.getMessage() + .contains(TreeSeries.class.getSimpleName())); + Assertions.assertTrue( + exception.getMessage().contains("is not supported")); + } + + @Test + void getPointValue_treeSeries_unsupported() { + TreeSeries series = setupTreeSeries(); + + UnsupportedOperationException exception = Assertions.assertThrows( + UnsupportedOperationException.class, + () -> tester.getValuesFromSeries(series)); + Assertions.assertTrue(exception.getMessage() + .contains(TreeSeries.class.getSimpleName())); + Assertions.assertTrue( + exception.getMessage().contains("is not supported")); + } + + @SuppressWarnings("unchecked") + @Test + void getValuesFromSeries_dataProviderSeries_getsValues() { + DataProviderSeries series = setupDataProviderSeries(); + Collection orders = ((ListDataProvider) series + .getDataProvider()).getItems(); + + Assertions.assertIterableEquals(tester.getValuesFromSeries(series), + orders.stream().map(Order::getTotalPrice) + .collect(Collectors.toList())); + } + + @Test + void getPointValue_dataProviderSeries_getsCorrectValue() { + DataProviderSeries series = setupDataProviderSeries(); + Collection orders = ((ListDataProvider) series + .getDataProvider()).getItems(); + for (Order item : orders) { + Assertions.assertEquals(item.getTotalPrice(), tester + .getPointValueFromSeries(series, item.getDescription())); + } + } + + @Test + void getPointValue_dataProviderSeries_notExistingThrows() { + DataProviderSeries series = setupDataProviderSeries(); + IllegalArgumentException exception = Assertions.assertThrows( + IllegalArgumentException.class, + () -> tester.getPointValueFromSeries(series, "XYZ")); + Assertions.assertTrue(exception.getMessage().contains("XYZ")); + Assertions.assertTrue( + exception.getMessage().contains("Invalid name for X-Axis")); + } + + @Test + void getPointValue_dataProviderSeries_missingXAxisAttributeThrows() { + DataProviderSeries series = setupDataProviderSeries(); + series.setX(null); + IllegalArgumentException exception = Assertions.assertThrows( + IllegalArgumentException.class, + () -> tester.getPointValueFromSeries(series, "XYZ")); + Assertions.assertTrue(exception.getMessage().contains("XYZ")); + Assertions.assertTrue( + exception.getMessage().contains("Invalid name for X-Axis")); + } + + @Test + void getPointValue_dataProviderSeries_duplicatedXAxisAttributeThrows() { + DataProviderSeries series = setupDataProviderSeries(); + series.setX(order -> "XYZ"); + IllegalStateException exception = Assertions.assertThrows( + IllegalStateException.class, + () -> tester.getPointValueFromSeries(series, "XYZ")); + Assertions.assertTrue(exception.getMessage().contains("XYZ")); + Assertions.assertTrue(exception.getMessage() + .contains("multiple items with same X-Axis name")); + } + + @Test + void getValuesFromSeries_heatSeries_unsupported() { + HeatSeries series = setupHeatSeries(); + UnsupportedOperationException exception = Assertions.assertThrows( + UnsupportedOperationException.class, + () -> tester.getValuesFromSeries(series)); + Assertions.assertTrue(exception.getMessage() + .contains(HeatSeries.class.getSimpleName())); + Assertions.assertTrue( + exception.getMessage().contains("is not supported")); + } + + @Test + void getPointValue_heatSeries_unsupported() { + HeatSeries series = setupHeatSeries(); + + UnsupportedOperationException exception = Assertions.assertThrows( + UnsupportedOperationException.class, + () -> tester.getValuesFromSeries(series)); + Assertions.assertTrue(exception.getMessage() + .contains(HeatSeries.class.getSimpleName())); + Assertions.assertTrue( + exception.getMessage().contains("is not supported")); + } + + private ListSeries setupListSeries() { + ListSeries series = new ListSeries("Tokyo", 49.9, 71.5, 106.4, 129.2, + 144.0, 176.0, 135.6, 148.5, 216.4, 194.1, 95.6, 54.4); + Configuration config = tester.getComponent().getConfiguration(); + config.addSeries(series); + config.getxAxis().setCategories("Jan", "Feb", "Mar", "Apr", "May", + "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"); + return series; + } + + private HeatSeries setupHeatSeries() { + HeatSeries series = new HeatSeries("Sales per employee", + new Number[][] { { 0, 0, 0 }, { 0, 1, 19 }, { 0, 2, 8 }, + { 0, 3, 24 }, { 0, 4, 67 }, { 1, 0, 92 }, { 1, 1, 58 }, + { 1, 2, 78 }, { 1, 3, 117 }, { 1, 4, 48 }, { 2, 0, 35 }, + { 2, 1, 15 }, { 2, 2, 123 }, { 2, 3, 64 }, { 2, 4, 52 }, + { 3, 0, 72 }, { 3, 1, 132 }, { 3, 2, 114 }, + { 3, 3, 19 }, { 3, 4, 16 }, { 4, 0, 38 }, { 4, 1, 5 }, + { 4, 2, 8 }, { 4, 3, 117 }, { 4, 4, 115 }, { 5, 0, 88 }, + { 5, 1, 32 }, { 5, 2, 12 }, { 5, 3, 6 }, { 5, 4, 120 }, + { 6, 0, 13 }, { 6, 1, 44 }, { 6, 2, 88 }, { 6, 3, 98 }, + { 6, 4, 96 }, { 7, 0, 31 }, { 7, 1, 1 }, { 7, 2, 82 }, + { 7, 3, 32 }, { 7, 4, 30 }, { 8, 0, 85 }, { 8, 1, 97 }, + { 8, 2, 123 }, { 8, 3, 64 }, { 8, 4, 84 }, { 9, 0, 47 }, + { 9, 1, 114 }, { 9, 2, 31 }, { 9, 3, 48 }, + { 9, 4, 91 } }); + tester.getComponent().getConfiguration().addSeries(series); + return series; + } + + private TreeSeries setupTreeSeries() { + TreeSeries series = new TreeSeries(); + TreeSeriesItem apples = new TreeSeriesItem("A", "Apples"); + apples.setColorIndex(0); + TreeSeriesItem bananas = new TreeSeriesItem("B", "Bananas"); + bananas.setColorIndex(2); + TreeSeriesItem oranges = new TreeSeriesItem("O", "Oranges"); + oranges.setColorIndex(3); + TreeSeriesItem anneA = new TreeSeriesItem("Anne", apples, 5); + TreeSeriesItem rickA = new TreeSeriesItem("Rick", apples, 3); + TreeSeriesItem peterA = new TreeSeriesItem("Peter", apples, 4); + TreeSeriesItem anneB = new TreeSeriesItem("Anne", bananas, 4); + TreeSeriesItem rickB = new TreeSeriesItem("Rick", bananas, 10); + TreeSeriesItem peterB = new TreeSeriesItem("Peter", bananas, 1); + TreeSeriesItem anneO = new TreeSeriesItem("Anne", oranges, 1); + TreeSeriesItem rickO = new TreeSeriesItem("Rick", oranges, 3); + TreeSeriesItem peterO = new TreeSeriesItem("Peter", oranges, 3); + TreeSeriesItem susanne = new TreeSeriesItem("Susanne", 2); + susanne.setParent("Kiwi"); + susanne.setColorIndex(4); + series.addAll(apples, bananas, oranges, anneA, rickA, peterA, anneB, + rickB, peterB, anneO, rickO, peterO, susanne); + tester.getComponent().getConfiguration().addSeries(series); + return series; + } + + private DataSeries createDataSeries() { + DataSeries series = new DataSeries(); + series.add(new DataSeriesItem("Chrome", 61.41)); + series.add(new DataSeriesItem("Internet Explorer", 11.84)); + series.add(new DataSeriesItem("Firefox", 10.85)); + series.add(new DataSeriesItem("Edge", 4.67)); + series.add(new DataSeriesItem("Safari", 4.18)); + series.add(new DataSeriesItem("Sogou Explorer", 1.64)); + series.add(new DataSeriesItem("Opera", 6.2)); + series.add(new DataSeriesItem("QQ", 1.2)); + series.add(new DataSeriesItem("Others", 2.61)); + tester.getComponent().getConfiguration().addSeries(series); + return series; + } + + private DataProviderSeries setupDataProviderSeries() { + List orders = new ArrayList<>(); + orders.add(new Order("Domain Name", 3, 7.99)); + orders.add(new Order("SSL Certificate", 1, 119.00)); + orders.add(new Order("Web Hosting", 1, 19.95)); + orders.add(new Order("Email Box", 20, 0.15)); + orders.add(new Order("E-Commerce Setup", 1, 25.00)); + orders.add(new Order("Technical Support", 1, 50.00)); + DataProvider dataProvider = new ListDataProvider<>(orders); + DataProviderSeries series = new DataProviderSeries<>( + dataProvider, Order::getTotalPrice); + series.setX(Order::getDescription); + tester.getComponent().getConfiguration().addSeries(series); + return series; + } + + private static class Order { + + private final String description; + private final int quantity; + private final double unitPrice; + + public Order(String description, int quantity, double unitPrice) { + this.description = description; + this.quantity = quantity; + this.unitPrice = unitPrice; + } + + public String getDescription() { + return description; + } + + public int getQuantity() { + return quantity; + } + + public double getUnitPrice() { + return unitPrice; + } + + public double getTotalPrice() { + return unitPrice * quantity; + } + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/charts/ChartTesterTest.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/charts/ChartTesterTest.java new file mode 100644 index 000000000..209e2395d --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/charts/ChartTesterTest.java @@ -0,0 +1,276 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.charts; + +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.vaadin.flow.component.charts.model.ListSeries; +import com.vaadin.flow.component.charts.model.Series; +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.testbench.unit.CommercialTesterWrappers; +import com.vaadin.testbench.unit.UIUnitTest; +import com.vaadin.testbench.unit.ViewPackages; + +@ViewPackages +class ChartTesterTest extends UIUnitTest implements CommercialTesterWrappers { + + ColumnChartView view; + + @BeforeEach + public void registerView() { + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(ColumnChartView.class); + view = navigate(ColumnChartView.class); + } + + @Test + void getSeriesValues_notUsable_throws() { + view.chart.setVisible(false); + ChartTester chartTester = test(view.chart); + IllegalStateException exception = Assertions.assertThrows( + IllegalStateException.class, + () -> chartTester.getSeriesValues(0)); + Assertions.assertTrue(exception.getMessage().contains("not usable")); + exception = Assertions.assertThrows(IllegalStateException.class, + () -> chartTester.getSeriesValues("Berlin")); + Assertions.assertTrue(exception.getMessage().contains("not usable")); + } + + @Test + void getSeriesValues_byIndex_getsSeriesValues() { + Assertions.assertIterableEquals(test(view.chart).getSeriesValues(0), + List.of(view.tokyo.getData())); + Assertions.assertIterableEquals(test(view.chart).getSeriesValues(1), + List.of(view.newYork.getData())); + Assertions.assertIterableEquals(test(view.chart).getSeriesValues(2), + List.of(view.london.getData())); + Assertions.assertIterableEquals(test(view.chart).getSeriesValues(3), + List.of(view.berlin.getData())); + } + + @Test + void getSeriesValues_byName_getsSeriesValues() { + Assertions.assertIterableEquals( + test(view.chart).getSeriesValues("Tokyo"), + List.of(view.tokyo.getData())); + Assertions.assertIterableEquals( + test(view.chart).getSeriesValues("New York"), + List.of(view.newYork.getData())); + Assertions.assertIterableEquals( + test(view.chart).getSeriesValues("London"), + List.of(view.london.getData())); + Assertions.assertIterableEquals( + test(view.chart).getSeriesValues("Berlin"), + List.of(view.berlin.getData())); + } + + @Test + void getSeriesValues_invalidIndex_throws() { + ChartTester chartTester = test(view.chart); + Assertions.assertThrows(IndexOutOfBoundsException.class, + () -> chartTester.getSeriesValues(-1)); + Assertions.assertThrows(IndexOutOfBoundsException.class, + () -> chartTester.getSeriesValues(5)); + } + + @Test + void getSeriesValues_notExistingSeries_throws() { + ChartTester chartTester = test(view.chart); + IllegalArgumentException exception = Assertions.assertThrows( + IllegalArgumentException.class, + () -> chartTester.getSeriesValues("Rome")); + Assertions.assertTrue(exception.getMessage().contains("Rome")); + Assertions + .assertTrue(exception.getMessage().contains("does not exists")); + } + + @Test + void getSeriesValues_duplicatedSeries_throws() { + String seriesName = view.tokyo.getName(); + ListSeries duplicated = new ListSeries(seriesName, + view.tokyo.getData()); + view.chart.getConfiguration().addSeries(duplicated); + ChartTester chartTester = test(view.chart); + IllegalStateException exception = Assertions.assertThrows( + IllegalStateException.class, + () -> chartTester.getSeriesValues(seriesName)); + Assertions.assertTrue(exception.getMessage().contains(seriesName)); + Assertions + .assertTrue(exception.getMessage().contains("Multiple series")); + } + + @Test + void getPointValue_byIndex_getsPointValue() { + for (int i = 0; i < view.categories.length; i++) { + Assertions.assertEquals( + test(view.chart).getPointValue(2, view.categories[i]), + view.london.getData()[i], + "Point " + view.categories[i] + " from series " + i); + } + } + + @Test + void getPointValue_byName_getsPointValue() { + for (int i = 0; i < view.categories.length; i++) { + Assertions + .assertEquals( + test(view.chart).getPointValue( + view.london.getName(), view.categories[i]), + view.london.getData()[i]); + } + } + + @Test + void getPointValue_notExistingItemName_throws() { + ChartTester chartTester = test(view.chart); + IllegalArgumentException exception = Assertions.assertThrows( + IllegalArgumentException.class, + () -> chartTester.getPointValue("Berlin", "XYZ")); + Assertions.assertTrue(exception.getMessage().contains("XYZ")); + Assertions.assertTrue(exception.getMessage().contains("Invalid")); + exception = Assertions.assertThrows(IllegalArgumentException.class, + () -> chartTester.getPointValue(0, "XYZ")); + Assertions.assertTrue(exception.getMessage().contains("XYZ")); + Assertions.assertTrue(exception.getMessage().contains("Invalid")); + } + + @Test + void getPointValue_notUsable_throws() { + view.chart.setVisible(false); + ChartTester chartTester = test(view.chart); + IllegalStateException exception = Assertions.assertThrows( + IllegalStateException.class, + () -> chartTester.getPointValue(0, "Jan")); + Assertions.assertTrue(exception.getMessage().contains("not usable")); + exception = Assertions.assertThrows(IllegalStateException.class, + () -> chartTester.getPointValue("Berlin", "Jan")); + Assertions.assertTrue(exception.getMessage().contains("not usable")); + } + + @Test + void getPointValue_duplicatedName_throws() { + view.categories[2] = "Feb"; + view.chart.getConfiguration().getxAxis().setCategories(view.categories); + + ChartTester chartTester = test(view.chart); + IllegalStateException exception = Assertions.assertThrows( + IllegalStateException.class, + () -> chartTester.getPointValue(0, "Feb")); + Assertions.assertTrue(exception.getMessage().contains("Feb")); + Assertions.assertTrue(exception.getMessage().contains("multiple")); + exception = Assertions.assertThrows(IllegalStateException.class, + () -> chartTester.getPointValue("Berlin", "Feb")); + Assertions.assertTrue(exception.getMessage().contains("Feb")); + Assertions.assertTrue(exception.getMessage().contains("multiple")); + } + + @Test + void clickLegendItem_notUsable_throws() { + view.chart.setVisible(false); + ChartTester chartTester = test(view.chart); + IllegalStateException exception = Assertions.assertThrows( + IllegalStateException.class, + () -> chartTester.clickLegendItem("Berlin")); + Assertions.assertTrue(exception.getMessage().contains("not usable")); + } + + @Test + void clickLegendItem_notExisting_throws() { + ChartTester chartTester = test(view.chart); + IllegalArgumentException exception = Assertions.assertThrows( + IllegalArgumentException.class, + () -> chartTester.clickLegendItem("Rome")); + Assertions.assertTrue(exception.getMessage().contains("Rome")); + Assertions + .assertTrue(exception.getMessage().contains("does not exist")); + } + + @Test + void clickLegendItem_eventFired() { + // SeriesLegendItemClickEvent + // PointLegendItemClickEvent ??? + AtomicReference seriesLegend = new AtomicReference<>(); + view.chart.addSeriesLegendItemClickListener( + ev -> seriesLegend.set(ev.getSeries())); + + test(view.chart).clickLegendItem("Berlin"); + Assertions.assertSame(view.berlin, seriesLegend.get()); + } + + @Test + void clickPoint_notUsable_throws() { + view.chart.setVisible(false); + ChartTester chartTester = test(view.chart); + IllegalStateException exception = Assertions.assertThrows( + IllegalStateException.class, + () -> chartTester.clickPoint("Berlin", "Feb")); + Assertions.assertTrue(exception.getMessage().contains("not usable")); + } + + @Test + void clickPoint_notExisting_throws() { + ChartTester chartTester = test(view.chart); + IllegalArgumentException exception = Assertions.assertThrows( + IllegalArgumentException.class, + () -> chartTester.clickPoint("Rome", "Feb")); + Assertions.assertTrue(exception.getMessage().contains("Rome")); + Assertions + .assertTrue(exception.getMessage().contains("does not exist")); + + exception = Assertions.assertThrows(IllegalArgumentException.class, + () -> chartTester.clickPoint("London", "XYZ")); + Assertions.assertTrue(exception.getMessage().contains("XYZ")); + Assertions.assertTrue(exception.getMessage().contains("Invalid")); + } + + @Test + void clickPoint_eventsFired() { + // SeriesClick + // PointClick + AtomicReference series = new AtomicReference<>(); + AtomicReference pointSeries = new AtomicReference<>(); + AtomicInteger point = new AtomicInteger(); + view.chart.addSeriesClickListener(ev -> series.set(ev.getSeries())); + view.chart.addPointClickListener(ev -> { + pointSeries.set(ev.getSeries()); + point.set(ev.getItemIndex()); + }); + + test(view.chart).clickPoint("Berlin", "Feb"); + Assertions.assertSame(view.berlin, series.get()); + Assertions.assertSame(view.berlin, pointSeries.get()); + Assertions.assertEquals(1, point.get()); + } + + @Test + void clickChart_notUsable_throws() { + view.chart.setVisible(false); + ChartTester chartTester = test(view.chart); + IllegalStateException exception = Assertions.assertThrows( + IllegalStateException.class, chartTester::clickChart); + Assertions.assertTrue(exception.getMessage().contains("not usable")); + } + + @Test + void clickChart_eventFired() { + AtomicBoolean chartClicked = new AtomicBoolean(); + view.chart.addChartClickListener(ev -> chartClicked.set(true)); + + test(view.chart).clickChart(); + Assertions.assertTrue(chartClicked.get()); + } + +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/charts/ColumnChartView.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/charts/ColumnChartView.java new file mode 100644 index 000000000..d4cd5ee70 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/charts/ColumnChartView.java @@ -0,0 +1,69 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.charts; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.HasComponents; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.component.charts.model.ChartType; +import com.vaadin.flow.component.charts.model.Configuration; +import com.vaadin.flow.component.charts.model.Crosshair; +import com.vaadin.flow.component.charts.model.ListSeries; +import com.vaadin.flow.component.charts.model.Tooltip; +import com.vaadin.flow.component.charts.model.XAxis; +import com.vaadin.flow.component.charts.model.YAxis; +import com.vaadin.flow.router.Route; + +@Tag("div") +@Route(value = "column-chart", registerAtStartup = false) +public class ColumnChartView extends Component implements HasComponents { + + final Chart chart; + final ListSeries tokyo = new ListSeries("Tokyo", 49.9, 71.5, 106.4, 129.2, + 144.0, 176.0, 135.6, 148.5, 216.4, 194.1, 95.6, 54.4); + final ListSeries newYork = new ListSeries("New York", 83.6, 78.8, 98.5, + 93.4, 106.0, 84.5, 105.0, 104.3, 91.2, 83.5, 106.6, 92.3); + final ListSeries london = new ListSeries("London", 48.9, 38.8, 39.3, 41.4, + 47.0, 48.3, 59.0, 59.6, 52.4, 65.2, 59.3, 51.2); + final ListSeries berlin = new ListSeries("Berlin", 42.4, 33.2, 34.5, 39.7, + 52.6, 75.5, 57.4, 60.4, 47.6, 39.1, 46.8, 51.1); + final String[] categories = new String[] { "Jan", "Feb", "Mar", "Apr", + "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + + public ColumnChartView() { + chart = new Chart(); + + Configuration configuration = chart.getConfiguration(); + configuration.setTitle("Monthly Average Rainfall"); + configuration.setSubTitle("Source: WorldClimate.com"); + chart.getConfiguration().getChart().setType(ChartType.COLUMN); + + configuration.addSeries(tokyo); + configuration.addSeries(newYork); + configuration.addSeries(london); + configuration.addSeries(berlin); + + XAxis x = new XAxis(); + x.setCrosshair(new Crosshair()); + x.setCategories(categories); + configuration.addxAxis(x); + + YAxis y = new YAxis(); + y.setMin(0); + y.setTitle("Rainfall (mm)"); + configuration.addyAxis(y); + + Tooltip tooltip = new Tooltip(); + tooltip.setShared(true); + configuration.setTooltip(tooltip); + + add(chart); + } + +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/checkbox/CheckboxGroupTesterTest.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/checkbox/CheckboxGroupTesterTest.java new file mode 100644 index 000000000..25a786a4f --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/checkbox/CheckboxGroupTesterTest.java @@ -0,0 +1,187 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.checkbox; + +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.testbench.unit.UIUnitTest; +import com.vaadin.testbench.unit.ViewPackages; + +@ViewPackages +class CheckboxGroupTesterTest extends UIUnitTest { + + CheckboxView view; + + @BeforeEach + public void registerView() { + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(CheckboxView.class); + view = navigate(CheckboxView.class); + } + + @Test + void selectItem_selectCorrectItem() { + test(view.checkboxGroup).selectItem("test-bar"); + assertContainsExactlyInAnyOrder(Set.of(view.items.get("bar")), + test(view.checkboxGroup).getSelected()); + + test(view.checkboxGroup).selectItem("test-jay"); + assertContainsExactlyInAnyOrder( + Set.of(view.items.get("bar"), view.items.get("jay")), + test(view.checkboxGroup).getSelected()); + } + + @Test + void selectItems_multipleItems_itemsSelected() { + test(view.checkboxGroup).selectItems("test-bar", "test-jay"); + assertContainsExactlyInAnyOrder( + Set.of(view.items.get("bar"), view.items.get("jay")), + test(view.checkboxGroup).getSelected()); + } + + @Test + void selectItems_collectionOfItems_itemsSelected() { + test(view.checkboxGroup).selectItems(List.of("test-bar", "test-jay")); + assertContainsExactlyInAnyOrder( + Set.of(view.items.get("bar"), view.items.get("jay")), + test(view.checkboxGroup).getSelected()); + } + + @Test + void selectAll_allItemSelected() { + test(view.checkboxGroup).selectAll(); + assertContainsExactlyInAnyOrder(view.items.values(), + test(view.checkboxGroup).getSelected()); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Test + void deselectItem_deselectCorrectItem() { + view.checkboxGroup.setValue(new HashSet<>(view.items.values())); + + test(view.checkboxGroup).deselectItem("test-bar"); + Assertions.assertEquals( + Set.of(view.items.get("foo"), view.items.get("baz"), + view.items.get("jay")), + test(view.checkboxGroup).getSelected()); + + test(view.checkboxGroup).deselectItem("test-jay"); + assertContainsExactlyInAnyOrder( + Set.of(view.items.get("foo"), view.items.get("baz")), + test(view.checkboxGroup).getSelected()); + + } + + @Test + void deselectItems_multipleItems_itemsDeselected() { + view.checkboxGroup.setValue(new HashSet<>(view.items.values())); + + test(view.checkboxGroup).deselectItems("test-jay", "test-bar"); + assertContainsExactlyInAnyOrder( + Set.of(view.items.get("foo"), view.items.get("baz")), + test(view.checkboxGroup).getSelected()); + } + + @Test + void deselectItems_collectionOfItems_itemsDeselected() { + view.checkboxGroup.setValue(new HashSet<>(view.items.values())); + + test(view.checkboxGroup).deselectItems(List.of("test-jay", "test-bar")); + assertContainsExactlyInAnyOrder( + Set.of(view.items.get("foo"), view.items.get("baz")), + test(view.checkboxGroup).getSelected()); + } + + @Test + void deselectAll_noItemsSelected() { + view.checkboxGroup.setValue(new HashSet<>(view.items.values())); + + test(view.checkboxGroup).deselectAll(); + Set selectedItems = test(view.checkboxGroup) + .getSelected(); + Assertions.assertTrue(selectedItems.isEmpty(), + "Expecting no elements to be selected, but got " + + selectedItems); + } + + @Test + void selectItem_notExisting_throws() { + Assertions.assertThrows(IllegalArgumentException.class, + () -> test(view.checkboxGroup).selectItem("jay")); + } + + @Test + void deselectItem_notExisting_throws() { + Assertions.assertThrows(IllegalArgumentException.class, + () -> test(view.checkboxGroup).deselectItem("jay")); + } + + @Test + void selectItem_itemDisabled_throws() { + view.checkboxGroup + .setItemEnabledProvider(n -> n.getName().startsWith("b")); + + // Items enabled, should work + test(view.checkboxGroup).selectItem("test-bar"); + test(view.checkboxGroup).selectItem("test-baz"); + + Assertions.assertThrows(IllegalStateException.class, + () -> test(view.checkboxGroup).selectItem("test-foo")); + Assertions.assertThrows(IllegalStateException.class, + () -> test(view.checkboxGroup).selectItem("test-jay")); + } + + @Test + void deselectItem_itemDisabled_throws() { + view.checkboxGroup + .setItemEnabledProvider(n -> n.getName().startsWith("b")); + + // Items enabled, should work + test(view.checkboxGroup).deselectItem("test-bar"); + test(view.checkboxGroup).deselectItem("test-baz"); + + Assertions.assertThrows(IllegalStateException.class, + () -> test(view.checkboxGroup).deselectItem("test-foo")); + Assertions.assertThrows(IllegalStateException.class, + () -> test(view.checkboxGroup).deselectItem("test-jay")); + } + + @Test + void readOnly_isNotUsable() { + view.checkboxGroup.setReadOnly(true); + + Assertions.assertThrows(IllegalStateException.class, + () -> test(view.checkboxGroup).selectItem("test-foo")); + Assertions.assertThrows(IllegalStateException.class, + () -> test(view.checkboxGroup).deselectItem("test-foo")); + Assertions.assertThrows(IllegalStateException.class, + () -> test(view.checkboxGroup).selectAll()); + Assertions.assertThrows(IllegalStateException.class, + () -> test(view.checkboxGroup).deselectAll()); + + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + private void assertContainsExactlyInAnyOrder(Collection expected, + Collection actual) { + Assertions.assertEquals(expected.size(), actual.size(), "Expected " + + expected.size() + " elements, but got " + actual.size()); + Assertions.assertTrue( + expected.containsAll(actual) && actual.containsAll(expected)); + } + +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/checkbox/CheckboxTesterTest.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/checkbox/CheckboxTesterTest.java new file mode 100644 index 000000000..9633c1255 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/checkbox/CheckboxTesterTest.java @@ -0,0 +1,99 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.checkbox; + +import java.util.concurrent.atomic.AtomicBoolean; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.testbench.unit.UIUnitTest; +import com.vaadin.testbench.unit.ViewPackages; + +@ViewPackages +class CheckboxTesterTest extends UIUnitTest { + + CheckboxView view; + + @BeforeEach + public void registerView() { + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(CheckboxView.class); + view = navigate(CheckboxView.class); + } + + @Test + void readOnlyCheckbox_isNotUsable() { + view.checkbox.setReadOnly(true); + Assertions.assertFalse(test(view.checkbox).isUsable(), + "Readonly checkbox should not be usable"); + } + + @Test + void click_usable_valueChanges() { + Assertions.assertFalse(view.checkbox.getValue(), + "Expecting checkbox initial state not to be checked"); + + test(view.checkbox).click(); + Assertions.assertTrue(view.checkbox.getValue(), + "Expecting checkbox to be checked, but was not"); + + test(view.checkbox).click(); + Assertions.assertFalse(view.checkbox.getValue(), + "Expecting checkbox not to be checked, but was"); + + } + + @Test + void click_usable_checkedChangeFired() { + AtomicBoolean checkedChange = new AtomicBoolean(); + view.checkbox.getElement().addPropertyChangeListener("checked", + ev -> checkedChange.set(true)); + + Assertions.assertFalse(view.checkbox.getValue(), + "Expecting checkbox not to be checked, but was"); + + test(view.checkbox).click(); + Assertions.assertTrue(checkedChange.get(), + "Expected checked change event to be fired, but was not"); + Assertions.assertTrue(view.checkbox.getValue(), + "Expecting checkbox not to be checked, but was"); + } + + @Test + void click_disabled_throws() { + view.checkbox.setEnabled(false); + Assertions.assertThrows(IllegalStateException.class, + test(view.checkbox)::click); + } + + @Test + void click_disabledByProperty_throws() { + view.checkbox.setDisabled(true); + Assertions.assertThrows(IllegalStateException.class, + test(view.checkbox)::click); + } + + @Test + void click_invisible_throws() { + view.checkbox.setVisible(false); + Assertions.assertThrows(IllegalStateException.class, + test(view.checkbox)::click); + } + + @Test + void click_readOnly_throws() { + view.checkbox.setReadOnly(true); + Assertions.assertThrows(IllegalStateException.class, + test(view.checkbox)::click); + } + +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/checkbox/CheckboxView.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/checkbox/CheckboxView.java new file mode 100644 index 000000000..6236096a0 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/checkbox/CheckboxView.java @@ -0,0 +1,58 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.checkbox; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.HasComponents; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.router.Route; + +@Tag("div") +@Route(value = "checkbox", registerAtStartup = false) +public class CheckboxView extends Component implements HasComponents { + + Checkbox checkbox = new Checkbox(); + + Map items = Stream.of("foo", "bar", "baz", "jay") + .collect(Collectors.toMap(Function.identity(), Name::new, + (a, b) -> a, LinkedHashMap::new)); + + CheckboxGroup checkboxGroup = new CheckboxGroup<>(); + + public CheckboxView() { + add(checkbox); + + checkboxGroup.setItems(items.values()); + checkboxGroup.setItemLabelGenerator(item -> "test-" + item); + add(checkboxGroup); + } + + public static class Name { + String name; + + public Name(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + @Override + public String toString() { + return name; + } + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/combobox/ComboBoxTesterTest.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/combobox/ComboBoxTesterTest.java new file mode 100644 index 000000000..13d309f07 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/combobox/ComboBoxTesterTest.java @@ -0,0 +1,70 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.combobox; + +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.testbench.unit.UIUnitTest; +import com.vaadin.testbench.unit.ViewPackages; + +@ViewPackages +public class ComboBoxTesterTest extends UIUnitTest { + + ComboBoxView view; + + @BeforeEach + void init() { + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(ComboBoxView.class); + view = navigate(ComboBoxView.class); + } + + @Test + void getSuggestionItems_noFilter_allItemsReturned() { + final List suggestions = test(view.combo) + .getSuggestionItems(); + Assertions.assertIterableEquals(view.items, suggestions); + } + + @Test + void getSuggestions_noFilter_allItemsReturned() { + final List suggestions = test(view.combo).getSuggestions(); + Assertions.assertIterableEquals(Arrays.asList("test-foo", "test-bar"), + suggestions); + } + + @Test + void setFilter_getSuggestions_filterIsApplied() { + test(view.combo).setFilter("fo"); + final List suggestions = test(view.combo).getSuggestions(); + Assertions.assertEquals(1, suggestions.size()); + Assertions.assertEquals("test-foo", suggestions.get(0)); + } + + @Test + void selectItem_selectsCorrectItem() { + Assertions.assertNull(test(view.combo).getSelected()); + + test(view.combo).selectItem("test-foo"); + + Assertions.assertSame(view.items.get(0), + test(view.combo).getSelected()); + + test(view.combo).selectItem(null); + + Assertions.assertNull(test(view.combo).getSelected(), + "Selecting null should clear selection"); + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/combobox/ComboBoxView.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/combobox/ComboBoxView.java new file mode 100644 index 000000000..824dcafa9 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/combobox/ComboBoxView.java @@ -0,0 +1,49 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.combobox; + +import java.util.Arrays; +import java.util.List; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.HasComponents; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.router.Route; + +@Tag("div") +@Route(value = "combo", registerAtStartup = false) +public class ComboBoxView extends Component implements HasComponents { + + ComboBox combo; + List items = Arrays.asList(new Name("foo"), new Name("bar")); + + public ComboBoxView() { + combo = new ComboBox<>("TestBox"); + combo.setItems(items); + combo.setItemLabelGenerator(item -> "test-" + item.toString()); + add(combo); + } + + public static class Name { + String name; + + public Name(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + @Override + public String toString() { + return name; + } + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/combobox/MultiSelectComboBoxTesterTest.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/combobox/MultiSelectComboBoxTesterTest.java new file mode 100644 index 000000000..c20f09593 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/combobox/MultiSelectComboBoxTesterTest.java @@ -0,0 +1,78 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.combobox; + +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.testbench.unit.UIUnitTest; +import com.vaadin.testbench.unit.ViewPackages; + +@ViewPackages +public class MultiSelectComboBoxTesterTest extends UIUnitTest { + + MultiSelectComboBoxView view; + + @BeforeEach + void init() { + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(MultiSelectComboBoxView.class); + view = navigate(MultiSelectComboBoxView.class); + } + + @Test + void getSuggestionItems_noFilter_allItemsReturned() { + final List suggestions = test(view.combo) + .getSuggestionItems(); + Assertions.assertIterableEquals(view.items, suggestions); + } + + @Test + void getSuggestions_noFilter_allItemsReturned() { + final List suggestions = test(view.combo).getSuggestions(); + Assertions.assertIterableEquals(Arrays.asList("test-foo", "test-bar"), + suggestions); + } + + @Test + void setFilter_getSuggestions_filterIsApplied() { + test(view.combo).setFilter("fo"); + final List suggestions = test(view.combo).getSuggestions(); + Assertions.assertEquals(1, suggestions.size()); + Assertions.assertEquals("test-foo", suggestions.get(0)); + } + + @Test + void selectItem_selectsCorrectItem() { + Assertions.assertTrue(test(view.combo).getSelected().isEmpty()); + + test(view.combo).selectItem("test-foo"); + + Assertions.assertIterableEquals(Set.of(view.items.get(0)), + test(view.combo).getSelected()); + + test(view.combo).selectItem("test-foo", "test-bar"); + + Assertions.assertTrue( + test(view.combo).getSelected().contains(view.items.get(0))); + Assertions.assertTrue( + test(view.combo).getSelected().contains(view.items.get(1))); + + test(view.combo).selectItem(null); + + Assertions.assertTrue(test(view.combo).getSelected().isEmpty(), + "Selecting null should clear selection"); + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/combobox/MultiSelectComboBoxView.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/combobox/MultiSelectComboBoxView.java new file mode 100644 index 000000000..e1d776341 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/combobox/MultiSelectComboBoxView.java @@ -0,0 +1,50 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.combobox; + +import java.util.Arrays; +import java.util.List; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.HasComponents; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.router.Route; + +@Tag("div") +@Route(value = "multicombo", registerAtStartup = false) +public class MultiSelectComboBoxView extends Component + implements HasComponents { + + MultiSelectComboBox combo; + List items = Arrays.asList(new Name("foo"), new Name("bar")); + + public MultiSelectComboBoxView() { + combo = new MultiSelectComboBox<>("TestBox"); + combo.setItems(items); + combo.setItemLabelGenerator(item -> "test-" + item.toString()); + add(combo); + } + + public static class Name { + String name; + + public Name(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + @Override + public String toString() { + return name; + } + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/confirmdialog/ConfirmDialogTesterTest.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/confirmdialog/ConfirmDialogTesterTest.java new file mode 100644 index 000000000..24d397fe6 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/confirmdialog/ConfirmDialogTesterTest.java @@ -0,0 +1,130 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.confirmdialog; + +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.testbench.unit.UIUnitTest; +import com.vaadin.testbench.unit.ViewPackages; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +@ViewPackages +class ConfirmDialogTesterTest extends UIUnitTest { + + ConfirmDialogView view; + ConfirmDialogTester wrap; + + @BeforeEach + void init() { + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(ConfirmDialogView.class); + view = navigate(ConfirmDialogView.class); + wrap = test(view.dialog); + } + + @Test + void cancelNotAvailable_throwsException() { + wrap.open(); + assertThrows(IllegalStateException.class, wrap::cancel); + } + + @Test + void rejectNotAvailable_throwsException() { + wrap.open(); + assertThrows(IllegalStateException.class, wrap::reject); + } + + @Test + void cancelAvailable_cancelEventClosesDialog() { + AtomicInteger close = new AtomicInteger(0); + view.dialog.setCancelButton("cancel", event -> close.incrementAndGet()); + wrap.open(); + + wrap.cancel(); + + Assertions.assertEquals(1, close.get()); + Assertions.assertFalse(view.dialog.isOpened()); + Assertions.assertFalse(view.dialog.isAttached(), + "Confirm dialog should be detached"); + } + + @Test + void rejectAvailable_cancelEventClosesDialog() { + AtomicInteger rejects = new AtomicInteger(0); + view.dialog.setRejectButton("reject", + event -> rejects.incrementAndGet()); + wrap.open(); + + wrap.reject(); + + Assertions.assertEquals(1, rejects.get()); + Assertions.assertFalse(view.dialog.isOpened()); + Assertions.assertFalse(view.dialog.isAttached(), + "Confirm dialog should be detached"); + } + + @Test + void confirmButton_ClosesDialog() { + AtomicInteger confirm = new AtomicInteger(0); + view.dialog.addConfirmListener(event -> confirm.incrementAndGet()); + wrap.open(); + + wrap.confirm(); + + Assertions.assertEquals(1, confirm.get()); + Assertions.assertFalse(view.dialog.isOpened()); + Assertions.assertFalse(view.dialog.isAttached(), + "Confirm dialog should be detached"); + } + + @Test + void programmaticallyClose_dialogIsDetached() { + wrap.open(); + + view.dialog.close(); + + Assertions.assertFalse(view.dialog.isAttached(), + "Confirm dialog should be detached"); + } + + @Test + void headerText_getHeaderReturnsCorrect() { + String header = "Test String"; + view.dialog.setHeader(header); + + wrap.open(); + + Assertions.assertEquals(header, wrap.getHeader()); + Assertions.assertThrows(IllegalStateException.class, + wrap::getHeaderElement); + } + + @Test + void headerElement_correctElementIsReturned() { + Head header = new Head(); + view.dialog.setHeader(header); + wrap.open(); + Assertions.assertEquals(header.getElement(), wrap.getHeaderElement()); + Assertions.assertNull(wrap.getHeader(), + "No string header should be available"); + } + + @Tag("div") + private static class Head extends Component { + + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/confirmdialog/ConfirmDialogView.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/confirmdialog/ConfirmDialogView.java new file mode 100644 index 000000000..885897b98 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/confirmdialog/ConfirmDialogView.java @@ -0,0 +1,25 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.confirmdialog; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.HasComponents; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.router.Route; + +@Tag("div") +@Route(value = "confirm-dialog", registerAtStartup = false) +public class ConfirmDialogView extends Component implements HasComponents { + + ConfirmDialog dialog; + + public ConfirmDialogView() { + dialog = new ConfirmDialog(); + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/contextmenu/ContextMenuTesterTest.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/contextmenu/ContextMenuTesterTest.java new file mode 100644 index 000000000..a8b65dd78 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/contextmenu/ContextMenuTesterTest.java @@ -0,0 +1,385 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.contextmenu; + +import java.util.List; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.vaadin.flow.component.html.Div; +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.testbench.unit.UIUnitTest; +import com.vaadin.testbench.unit.ViewPackages; + +@ViewPackages +class ContextMenuTesterTest extends UIUnitTest { + + ContextMenuView view; + + @BeforeEach + void init() { + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(ContextMenuView.class); + view = navigate(ContextMenuView.class); + } + + @Test + void openCloseMenu_menuIsAttachedAndDetached() { + Assertions.assertFalse(view.menu.isAttached(), + "closed context menu should not be attached to the UI"); + test(view.menu).open(); + Assertions.assertTrue(view.menu.isAttached(), + "context menu should be attached to the UI, but was not"); + + test(view.menu).close(); + Assertions.assertFalse(view.menu.isAttached(), + "context menu should be detached from the UI, but was not"); + } + + @Test + void programmaticallyClose_menuIsDetached() { + test(view.menu).open(); + + view.menu.close(); + + Assertions.assertFalse(view.menu.isAttached(), + "context menu should be detached from the UI, but was not"); + } + + @Test + void openMenu_alreadyOpen_throws() { + ContextMenuTester menu_ = test(view.menu); + menu_.open(); + IllegalStateException exception = Assertions + .assertThrows(IllegalStateException.class, menu_::open); + Assertions.assertTrue(exception.getMessage().contains("already open")); + } + + @Test + void closeMenu_menuNotOpened_throws() { + Assertions.assertThrows(IllegalStateException.class, + test(view.menu)::close); + } + + @Test + void clickItem_byText_actionExecuted() { + ContextMenuTester menu_ = test(view.menu); + menu_.open(); + + menu_.clickItem("Foo"); + Assertions.assertIterableEquals(List.of("Foo"), view.clickedItems); + + menu_.clickItem("Text"); + menu_.clickItem("Bar"); + Assertions.assertIterableEquals(List.of("Foo", "Text", "Bar"), + view.clickedItems); + } + + @Test + void clickItem_notExisting_throws() { + ContextMenuTester menu_ = test(view.menu); + menu_.open(); + + Assertions.assertThrows(IllegalArgumentException.class, + () -> menu_.clickItem("XYZ")); + } + + @Test + void clickItem_menuNotOpened_throws() { + Assertions.assertThrows(IllegalStateException.class, + () -> test(view.menu).clickItem("Bar")); + } + + @Test + void clickItem_multipleMatches_throws() { + ContextMenuTester menu_ = test(view.menu); + menu_.open(); + + IllegalStateException exception = Assertions.assertThrows( + IllegalStateException.class, + () -> menu_.clickItem("Duplicated")); + Assertions.assertTrue(exception.getMessage() + .contains("Expecting a single menu item")); + } + + @Test + void clickItem_checkable_checkStatusChanges() { + ContextMenuTester menu_ = test(view.menu); + menu_.open(); + + menu_.clickItem("Checkable"); + Assertions.assertIterableEquals(List.of("Checkable"), + view.clickedItems); + Assertions.assertTrue(view.checkableItem.isChecked(), + "Item should be checked but was not"); + + menu_.clickItem("Checkable"); + Assertions.assertIterableEquals(List.of("Checkable", "Checkable"), + view.clickedItems); + Assertions.assertFalse(view.checkableItem.isChecked(), + "Item should not be checked but was"); + } + + @Test + void clickItem_disabled_throws() { + ContextMenuTester menu_ = test(view.menu); + menu_.open(); + + IllegalStateException exception = Assertions.assertThrows( + IllegalStateException.class, () -> menu_.clickItem("Disabled")); + Assertions.assertTrue(exception.getMessage().contains("Menu item")); + Assertions.assertTrue(exception.getMessage().contains("is not usable")); + + Assertions.assertTrue(view.clickedItems.isEmpty(), + "Listener should not have been notified"); + } + + @Test + void clickItem_hidden_throws() { + ContextMenuTester menu_ = test(view.menu); + menu_.open(); + + IllegalStateException exception = Assertions.assertThrows( + IllegalStateException.class, () -> menu_.clickItem("Hidden")); + Assertions.assertTrue(exception.getMessage().contains("Menu item")); + Assertions.assertTrue(exception.getMessage().contains("is not usable")); + Assertions.assertTrue(view.clickedItems.isEmpty(), + "Listener should not have been notified"); + } + + @Test + void clickItem_nested_executeAction() { + ContextMenuTester menu_ = test(view.menu); + menu_.open(); + + menu_.clickItem("Hierarchical", "Level2"); + Assertions.assertIterableEquals(List.of("Hierarchical / Level2"), + view.clickedItems); + + view.clickedItems.clear(); + menu_.clickItem("Hierarchical", "NestedSubMenu", "Level3"); + Assertions.assertIterableEquals( + List.of("Hierarchical / NestedSubMenu / Level3"), + view.clickedItems); + } + + @Test + void clickItem_nestedWrongPath_throws() { + ContextMenuTester menu_ = test(view.menu); + menu_.open(); + + Assertions.assertThrows(IllegalArgumentException.class, + () -> menu_.clickItem("Foo", "Bar")); + Assertions.assertThrows(IllegalArgumentException.class, + () -> menu_.clickItem("Hierarchical", "Level3")); + Assertions.assertThrows(IllegalArgumentException.class, () -> menu_ + .clickItem("Hierarchical", "NestedSubMenu", "Level3Bis")); + Assertions.assertThrows(IllegalArgumentException.class, + () -> menu_.clickItem("Hierarchical", "NestedSubMenu", "Level3", + "Level4")); + } + + @Test + void clickItem_nestedNotUsableParent_throws() { + ContextMenuTester menu_ = test(view.menu); + menu_.open(); + + IllegalStateException exception = Assertions + .assertThrows(IllegalStateException.class, () -> menu_ + .clickItem("Hierarchical", "NestedDisabled", "Level3")); + Assertions.assertTrue(exception.getMessage().contains("Menu item")); + Assertions.assertTrue(exception.getMessage().contains("is not usable")); + + exception = Assertions.assertThrows(IllegalStateException.class, + () -> menu_.clickItem("Hierarchical", "NestedInvisible", + "Level3")); + Assertions.assertTrue(exception.getMessage().contains("Menu item")); + Assertions.assertTrue(exception.getMessage().contains("is not usable")); + } + + @Test + void clickItem_byIndex_executesAction() { + ContextMenuTester menu_ = test(view.menu); + menu_.open(); + + menu_.clickItem(0); + Assertions.assertIterableEquals(List.of("Foo"), view.clickedItems); + + menu_.clickItem(2); + menu_.clickItem(1); + Assertions.assertIterableEquals(List.of("Foo", "Text", "Bar"), + view.clickedItems); + + } + + @Test + void clickItem_byInvalidIndexes_throws() { + ContextMenuTester menu_ = test(view.menu); + menu_.open(); + + Assertions.assertThrows(IllegalArgumentException.class, + () -> menu_.clickItem(1, 2)); + Assertions.assertThrows(IllegalArgumentException.class, + () -> menu_.clickItem(8, 5)); + Assertions.assertThrows(IllegalArgumentException.class, + () -> menu_.clickItem(8, 1, 5)); + Assertions.assertThrows(IllegalArgumentException.class, + () -> menu_.clickItem(8, 1, 0, 4)); + } + + @Test + void clickItem_byNestedIndexNotUsableParent_throws() { + // Hierarchical / NestedDisabled / Level3 + test(view.menu).open(); + + IllegalStateException exception = Assertions.assertThrows( + IllegalStateException.class, + () -> test(view.menu).clickItem(7, 3, 0)); + Assertions.assertTrue(exception.getMessage().contains("Menu item")); + Assertions.assertTrue(exception.getMessage().contains("is not usable")); + } + + @Test + void clickItem_byNestedIndexes_executesAction() { + ContextMenuTester menu_ = test(view.menu); + menu_.open(); + + menu_.clickItem(8, 0); + Assertions.assertIterableEquals(List.of("Hierarchical / Level2"), + view.clickedItems); + + view.clickedItems.clear(); + menu_.clickItem(8, 1, 0); + Assertions.assertIterableEquals( + List.of("Hierarchical / NestedSubMenu / Level3"), + view.clickedItems); + + } + + @Test + void isItemChecked_byText_getCheckedStatus() { + ContextMenuTester menu_ = test(view.menu); + menu_.open(); + + Assertions.assertFalse(menu_.isItemChecked("Checkable"), + "Checkable item should not be checked by default, but result is true"); + + view.checkableItem.setChecked(true); + Assertions.assertTrue(menu_.isItemChecked("Checkable"), + "Checkable item is checked, but result is false"); + + view.checkableItem.setChecked(false); + Assertions.assertFalse(menu_.isItemChecked("Checkable"), + "Checkable item is not checked, but result is true"); + } + + @Test + void isItemChecked_byIndex_getCheckedStatus() { + ContextMenuTester menu_ = test(view.menu); + menu_.open(); + + Assertions.assertFalse(menu_.isItemChecked(6), + "Checkable item should not be checked by default, but result is true"); + + view.checkableItem.setChecked(true); + Assertions.assertTrue(menu_.isItemChecked(6), + "Checkable item is checked, but result is false"); + + view.checkableItem.setChecked(false); + Assertions.assertFalse(menu_.isItemChecked(6), + "Checkable item is not checked, but result is true"); + } + + @Test + void isItemChecked_nestedByText_getCheckedStatus() { + ContextMenuTester menu_ = test(view.menu); + menu_.open(); + + Assertions.assertTrue( + menu_.isItemChecked("Hierarchical", "Nested Checkable"), + "Checkable item should be checked by default, but result is false"); + + view.nestedCheckableItem.setChecked(false); + Assertions.assertFalse( + menu_.isItemChecked("Hierarchical", "Nested Checkable"), + "Checkable item is not checked, but result is true"); + + view.nestedCheckableItem.setChecked(true); + Assertions.assertTrue( + menu_.isItemChecked("Hierarchical", "Nested Checkable"), + "Checkable item is checked, but result is false"); + } + + @Test + void isItemChecked_nestedByIndex_getCheckedStatus() { + ContextMenuTester menu_ = test(view.menu); + menu_.open(); + + Assertions.assertTrue(menu_.isItemChecked(8, 2), + "Checkable item should be checked by default, but result is false"); + + view.nestedCheckableItem.setChecked(false); + Assertions.assertFalse(menu_.isItemChecked(8, 2), + "Checkable item is not checked, but result is true"); + + view.nestedCheckableItem.setChecked(true); + Assertions.assertTrue(menu_.isItemChecked(8, 2), + "Checkable item is checked, but result is false"); + } + + @Test + void isItemChecked_notCheckableItem_throws() { + ContextMenuTester menu_ = test(view.menu); + menu_.open(); + + Assertions.assertThrows(IllegalArgumentException.class, + () -> menu_.isItemChecked("Bar")); + Assertions.assertThrows(IllegalArgumentException.class, + () -> menu_.isItemChecked("Hierarchical", "Level2")); + Assertions.assertThrows(IllegalArgumentException.class, + () -> menu_.isItemChecked(0)); + Assertions.assertThrows(IllegalArgumentException.class, + () -> menu_.isItemChecked(8, 1)); + } + + @Test + void isItemChecked_menuNotOpened_throws() { + Assertions.assertThrows(IllegalStateException.class, + () -> test(view.menu).isItemChecked("Checkable")); + Assertions.assertThrows(IllegalStateException.class, + () -> test(view.menu).isItemChecked(5)); + } + + @Test + void isItemChecked_notExisting_throws() { + ContextMenuTester menu_ = test(view.menu); + menu_.open(); + + Assertions.assertThrows(IllegalArgumentException.class, + () -> menu_.isItemChecked("XYZ")); + Assertions.assertThrows(IllegalArgumentException.class, + () -> menu_.isItemChecked(22)); + } + + @Test + void openAndFind_ContextMenuItemsCanBeAccessed() { + var menuTester = test(view.menu); + menuTester.open(); + var div = menuTester.find(Div.class).withText("Component Item") + .single(); + Assertions.assertTrue(div.isAttached()); + + menuTester.close(); + div = menuTester.find(Div.class).withText("Component Item").single(); + Assertions.assertFalse(div.isAttached()); + } + +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/contextmenu/ContextMenuView.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/contextmenu/ContextMenuView.java new file mode 100644 index 000000000..7032daae3 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/contextmenu/ContextMenuView.java @@ -0,0 +1,74 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.contextmenu; + +import java.util.ArrayList; +import java.util.List; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.HasComponents; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.component.html.Div; +import com.vaadin.flow.component.orderedlayout.VerticalLayout; +import com.vaadin.flow.router.Route; + +@Tag("div") +@Route(value = "contextmenu", registerAtStartup = false) +public class ContextMenuView extends Component implements HasComponents { + + final MenuItem checkableItem; + final MenuItem nestedCheckableItem; + final List clickedItems = new ArrayList<>(); + final ContextMenu menu; + + public ContextMenuView() { + Span assignee = new Span(); + menu = new ContextMenu(); + menu.setTarget(assignee); + menu.addItem("Foo", ev -> clickedItems.add("Foo")); + menu.addItem("Bar", ev -> clickedItems.add("Bar")); + menu.addItem("Text", ev -> clickedItems.add("Text")); + menu.addItem("Duplicated", ev -> clickedItems.add("Duplicated 1")); + menu.addItem("Duplicated", ev -> clickedItems.add("Duplicated 2")); + menu.addItem(new VerticalLayout(new Div("Component Item")), + click -> clickedItems.add("Component Item")); + checkableItem = menu.addItem("Checkable", + ev -> clickedItems.add("Checkable")); + checkableItem.setCheckable(true); + menu.addItem("Disabled", ev -> clickedItems.add("Disabled")) + .setEnabled(false); + menu.addItem("Hidden", ev -> clickedItems.add("Hidden")) + .setVisible(false); + + SubMenu subMenu = menu.addItem("Hierarchical").getSubMenu(); + subMenu.addItem("Level2", + ev -> clickedItems.add("Hierarchical / Level2")); + subMenu.addItem("NestedSubMenu").getSubMenu().addItem("Level3", + ev -> clickedItems + .add("Hierarchical / NestedSubMenu / Level3")); + nestedCheckableItem = subMenu.addItem("Nested Checkable"); + nestedCheckableItem.setCheckable(true); + nestedCheckableItem.setChecked(true); + MenuItem nestedDisabled = subMenu.addItem("NestedDisabled"); + nestedDisabled.setEnabled(false); + nestedDisabled.getSubMenu().addItem("Level3", ev -> clickedItems + .add("Hierarchical / NestedDisabled / Level3")); + MenuItem nestedInvisible = subMenu.addItem("NestedInvisible"); + nestedInvisible.setVisible(false); + nestedInvisible.getSubMenu().addItem("Level3", ev -> clickedItems + .add("Hierarchical / NestedInvisible / Level3")); + + add(assignee); + } + + @Tag("span") + private static class Span extends Component { + + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/datepicker/DatePickerTesterTest.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/datepicker/DatePickerTesterTest.java new file mode 100644 index 000000000..b0729931f --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/datepicker/DatePickerTesterTest.java @@ -0,0 +1,72 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.datepicker; + +import java.time.LocalDate; +import java.util.concurrent.atomic.AtomicReference; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.vaadin.flow.component.AbstractField; +import com.vaadin.flow.component.HasValue; +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.testbench.unit.UIUnitTest; +import com.vaadin.testbench.unit.ViewPackages; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +@ViewPackages +class DatePickerTesterTest extends UIUnitTest { + + DatePickerView view; + + @BeforeEach + public void registerView() { + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(DatePickerView.class); + view = navigate(DatePickerView.class); + } + + @Test + void invalidValue_overMaxDate_throwsIllegalArgument() { + view.picker.setMax(LocalDate.of(1995, 1, 1)); + + assertThrows(IllegalArgumentException.class, + () -> test(view.picker).setValue(LocalDate.of(1995, 1, 5))); + } + + @Test + void invalidValue_underMinDate_throwsIllegalArgument() { + view.picker.setMin(LocalDate.of(1995, 1, 5)); + + assertThrows(IllegalArgumentException.class, + () -> test(view.picker).setValue(LocalDate.of(1995, 1, 1))); + } + + @Test + void setValue_eventIsFired_valueIsSet() { + + AtomicReference value = new AtomicReference<>(null); + + view.picker.addValueChangeListener( + (HasValue.ValueChangeListener>) event -> { + if (event.isFromClient()) { + value.compareAndSet(null, event.getValue()); + } + }); + + final LocalDate newValue = LocalDate.of(1995, 1, 5); + test(view.picker).setValue(newValue); + + Assertions.assertEquals(newValue, value.get()); + } + +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/datepicker/DatePickerView.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/datepicker/DatePickerView.java new file mode 100644 index 000000000..d25aa3392 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/datepicker/DatePickerView.java @@ -0,0 +1,25 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.datepicker; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.HasComponents; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.router.Route; + +@Tag("div") +@Route(value = "date", registerAtStartup = false) +public class DatePickerView extends Component implements HasComponents { + DatePicker picker; + + public DatePickerView() { + picker = new DatePicker(); + add(picker); + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/datetimepicker/DateTimePickerTesterTest.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/datetimepicker/DateTimePickerTesterTest.java new file mode 100644 index 000000000..edcb674c1 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/datetimepicker/DateTimePickerTesterTest.java @@ -0,0 +1,78 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.datetimepicker; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.concurrent.atomic.AtomicReference; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.vaadin.flow.component.AbstractField; +import com.vaadin.flow.component.HasValue; +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.testbench.unit.UIUnitTest; +import com.vaadin.testbench.unit.ViewPackages; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +@ViewPackages +class DateTimePickerTesterTest extends UIUnitTest { + + DateTimePickerView view; + + @BeforeEach + public void registerView() { + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(DateTimePickerView.class); + view = navigate(DateTimePickerView.class); + } + + @Test + void invalidValue_overMaxDate_throwsIllegalArgument() { + view.picker.setMax( + LocalDateTime.of(LocalDate.of(1995, 1, 1), LocalTime.NOON)); + + assertThrows(IllegalArgumentException.class, + () -> test(view.picker).setValue(LocalDateTime + .of(LocalDate.of(1995, 1, 5), LocalTime.MIDNIGHT))); + } + + @Test + void invalidValue_underMinDate_throwsIllegalArgument() { + view.picker.setMin( + LocalDateTime.of(LocalDate.of(1995, 1, 5), LocalTime.NOON)); + + assertThrows(IllegalArgumentException.class, + () -> test(view.picker).setValue(LocalDateTime + .of(LocalDate.of(1995, 1, 1), LocalTime.MIDNIGHT))); + } + + @Test + void setValue_eventIsFired_valueIsSet() { + + AtomicReference value = new AtomicReference<>(null); + + view.picker.addValueChangeListener( + (HasValue.ValueChangeListener>) event -> { + if (event.isFromClient()) { + value.compareAndSet(null, event.getValue()); + } + }); + + final LocalDateTime newValue = LocalDateTime + .of(LocalDate.of(1995, 1, 5), LocalTime.NOON); + test(view.picker).setValue(newValue); + + Assertions.assertEquals(newValue, value.get()); + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/datetimepicker/DateTimePickerView.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/datetimepicker/DateTimePickerView.java new file mode 100644 index 000000000..853d9c5ae --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/datetimepicker/DateTimePickerView.java @@ -0,0 +1,25 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.datetimepicker; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.HasComponents; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.router.Route; + +@Tag("div") +@Route(value = "date-time", registerAtStartup = false) +public class DateTimePickerView extends Component implements HasComponents { + DateTimePicker picker; + + public DateTimePickerView() { + picker = new DateTimePicker(); + add(picker); + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/details/DetailsTesterTest.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/details/DetailsTesterTest.java new file mode 100644 index 000000000..39cd52a85 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/details/DetailsTesterTest.java @@ -0,0 +1,136 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.details; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.testbench.unit.UIUnitTest; +import com.vaadin.testbench.unit.ViewPackages; + +@ViewPackages +class DetailsTesterTest extends UIUnitTest { + + DetailsView view; + + @BeforeEach + public void registerView() { + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(DetailsView.class); + view = navigate(DetailsView.class); + } + + @Test + void openDetails_contentVisible() { + AtomicBoolean listenerInvoked = new AtomicBoolean(); + view.details.addOpenedChangeListener(ev -> listenerInvoked.set(true)); + + test(view.details).openDetails(); + Assertions.assertTrue(listenerInvoked.get()); + Assertions.assertTrue(view.details.isOpened(), + "Contents should be visible after opening details"); + } + + @Test + void openDetails_notUsable_throws() { + view.details.setEnabled(false); + Assertions.assertThrows(IllegalStateException.class, + test(view.details)::openDetails); + } + + @Test + void openDetails_alreadyOpen_throws() { + view.details.setOpened(true); + AtomicBoolean listenerInvoked = new AtomicBoolean(); + view.details.addOpenedChangeListener(ev -> listenerInvoked.set(true)); + + Assertions.assertThrows(IllegalStateException.class, + test(view.details)::openDetails); + Assertions.assertFalse(listenerInvoked.get()); + } + + @Test + void closeDetails_contentHidden() { + view.details.setOpened(true); + + AtomicBoolean listenerInvoked = new AtomicBoolean(); + view.details.addOpenedChangeListener(ev -> listenerInvoked.set(true)); + + test(view.details).closeDetails(); + Assertions.assertTrue(listenerInvoked.get()); + Assertions.assertFalse(view.details.isOpened(), + "Contents should not be visible after closing details"); + } + + @Test + void closeDetails_notUsable_throws() { + view.details.setEnabled(false); + Assertions.assertThrows(IllegalStateException.class, + test(view.details)::closeDetails); + } + + @Test + void closeDetails_alreadyClosed_throws() { + AtomicBoolean listenerInvoked = new AtomicBoolean(); + view.details.addOpenedChangeListener(ev -> listenerInvoked.set(true)); + + Assertions.assertThrows(IllegalStateException.class, + test(view.details)::closeDetails); + Assertions.assertFalse(listenerInvoked.get()); + } + + @Test + void toggleDetails_notUsable_throws() { + view.details.setEnabled(false); + Assertions.assertThrows(IllegalStateException.class, + test(view.details)::toggleDetails); + } + + @Test + void toggleDetails_detailsVisibilityChanges() { + List stateChanges = new ArrayList<>(); + view.details + .addOpenedChangeListener(ev -> stateChanges.add(ev.isOpened())); + + test(view.details).toggleDetails(); + Assertions.assertIterableEquals(List.of(true), stateChanges); + + test(view.details).toggleDetails(); + Assertions.assertIterableEquals(List.of(true, false), stateChanges); + + test(view.details).toggleDetails(); + Assertions.assertIterableEquals(List.of(true, false, true), + stateChanges); + } + + @Test + void isOpen_getsDetailsOpenInfo() { + Assertions.assertFalse(test(view.details).isOpen()); + + view.details.setOpened(true); + Assertions.assertTrue(test(view.details).isOpen()); + + view.details.setOpened(false); + Assertions.assertFalse(test(view.details).isOpen()); + } + + @Test + void isOpen_notUsable_throws() { + view.details.setEnabled(false); + Assertions.assertThrows(IllegalStateException.class, + test(view.details)::isOpen); + } + +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/details/DetailsView.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/details/DetailsView.java new file mode 100644 index 000000000..30f9502c7 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/details/DetailsView.java @@ -0,0 +1,33 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.details; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.HasComponents; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.router.Route; + +@Tag("div") +@Route(value = "details", registerAtStartup = false) +public class DetailsView extends Component implements HasComponents { + + Details details; + DetailsContents contents; + + public DetailsView() { + contents = new DetailsContents(); + details = new Details("Members", contents); + add(details); + } + + @Tag("div") + public static class DetailsContents extends Component { + + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/dialog/DialogTesterTest.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/dialog/DialogTesterTest.java new file mode 100644 index 000000000..d1f7c6102 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/dialog/DialogTesterTest.java @@ -0,0 +1,91 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.dialog; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.vaadin.flow.component.ModalityMode; +import com.vaadin.flow.component.button.Button; +import com.vaadin.flow.component.button.ButtonTester; +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.testbench.unit.UIUnitTest; +import com.vaadin.testbench.unit.ViewPackages; + +@ViewPackages +class DialogTesterTest extends UIUnitTest { + + DialogView view; + DialogTester dialog_; + + @BeforeEach + void init() { + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(DialogView.class); + view = navigate(DialogView.class); + dialog_ = test(view.dialog); + } + + @Test + void openAndClose_dialogIsAttachedAndDetached() { + dialog_.open(); + Assertions.assertTrue(dialog_.isUsable(), + "Dialog should be attached on open"); + + dialog_.close(); + Assertions.assertFalse(dialog_.isUsable(), + "Dialog should not be usable after close"); + Assertions.assertFalse(view.dialog.isAttached(), + "Dialog should be detached on close"); + } + + @Test + void programmaticallyClose_dialogIsDetached() { + dialog_.open(); + + view.dialog.close(); + + Assertions.assertFalse(view.dialog.isAttached(), + "Dialog should be detached on close"); + } + + @Test + void modalDialog_visual_doNotBlockUIComponents() { + view.dialog.setModality(ModalityMode.VISUAL); + dialog_.open(); + ButtonTester") + .withProperty("deceased", Person::getDeceased) + .withFunction("onClick", person -> { + person.setDeceased(!person.getDeceased()); + basicGrid.getListDataView().refreshItem(person); + }); + } + +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/BeanGridTesterTest.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/BeanGridTesterTest.java new file mode 100644 index 000000000..e45d3eb62 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/BeanGridTesterTest.java @@ -0,0 +1,60 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.grid; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.testbench.unit.UIUnitTest; +import com.vaadin.testbench.unit.ViewPackages; + +@ViewPackages +public class BeanGridTesterTest extends UIUnitTest { + + BeanGridView view; + GridTester, Person> grid_; + + @BeforeEach + void init() { + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(BeanGridView.class); + + view = navigate(BeanGridView.class); + grid_ = test(view.beanGrid); + } + + @Test + void beanGrid_assertBeanColumns() { + Assertions.assertEquals(2, grid_.size()); + + Assertions.assertTrue(grid_.getSelected().isEmpty()); + + final int firstName = grid_.getColumnPosition("firstName"); + final int age = grid_.getColumnPosition("age"); + + Assertions.assertEquals(view.first.getFirstName(), + grid_.getCellText(0, firstName)); + Assertions.assertEquals(Integer.toString(view.first.getAge()), + grid_.getCellText(0, age)); + Assertions.assertEquals(view.first.getLastName(), + grid_.getCellText(0, grid_.getColumnPosition("lastName"))); + Assertions.assertEquals(view.first.getEmail(), + grid_.getCellText(0, grid_.getColumnPosition("email"))); + Assertions.assertEquals(view.first.getAddress().toString(), + grid_.getCellText(0, grid_.getColumnPosition("address"))); + + Assertions.assertEquals(view.second.getFirstName(), + grid_.getCellText(1, firstName)); + Assertions.assertEquals(Integer.toString(view.second.getAge()), + grid_.getCellText(1, age)); + } + +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/BeanGridView.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/BeanGridView.java new file mode 100644 index 000000000..b50a4a281 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/BeanGridView.java @@ -0,0 +1,30 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.grid; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.HasComponents; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.router.Route; + +@Tag("div") +@Route(value = "basic-grid", registerAtStartup = false) +public class BeanGridView extends Component implements HasComponents { + + Grid beanGrid = new Grid<>(Person.class); + Person first = Person.createTestPerson1(); + Person second = Person.createTestPerson2(); + + public BeanGridView() { + beanGrid.setItems(first, second); + + add(beanGrid); + } + +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/CheckBox.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/CheckBox.java new file mode 100644 index 000000000..3a18cd7d4 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/CheckBox.java @@ -0,0 +1,29 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.grid; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.Tag; + +@Tag("check-box") +public class CheckBox extends Component { + boolean checked; + + public CheckBox(boolean checked) { + this.checked = checked; + } + + public boolean isChecked() { + return checked; + } + + public void setChecked(boolean checked) { + this.checked = checked; + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/Country.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/Country.java new file mode 100644 index 000000000..6fd393324 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/Country.java @@ -0,0 +1,33 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.grid; + +/** + * Copied from grid integration-tests. + */ +public enum Country { + + FINLAND("Finland"), + SWEDEN("Sweden"), + USA("USA"), + RUSSIA("Russia"), + NETHERLANDS("Netherlands"), + SOUTH_AFRICA("South Africa"); + + private String name; + + private Country(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/GetTextCellRendererTest.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/GetTextCellRendererTest.java new file mode 100644 index 000000000..8dbbd8a1d --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/GetTextCellRendererTest.java @@ -0,0 +1,52 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.grid; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.testbench.unit.UIUnitTest; +import com.vaadin.testbench.unit.ViewPackages; + +@ViewPackages +class GetTextCellRendererTest extends UIUnitTest { + + RendererGridView view; + GridTester, Person> grid_; + + @BeforeEach + void init() { + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(RendererGridView.class); + + view = navigate(RendererGridView.class); + grid_ = test(view.grid); + } + + @Test + void getCellText_componentRenderer_getTextRecursively() { + Assertions.assertEquals( + String.format("%s%s%d", view.first.getFirstName(), + view.first.getLastName(), view.first.getAge()), + grid_.getCellText(0, 0)); + } + + @Test + void getCellText_renderNull_getsNull() { + Assertions.assertNull(grid_.getCellText(0, 1)); + } + + @Test + void getCellText_nonTextComponent_getsEmptyString() { + Assertions.assertTrue(grid_.getCellText(0, 2).isEmpty()); + } + +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/GridTesterSortTest.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/GridTesterSortTest.java new file mode 100644 index 000000000..ea20a7bb5 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/GridTesterSortTest.java @@ -0,0 +1,263 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.grid; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.vaadin.flow.data.provider.SortDirection; +import com.vaadin.flow.data.provider.SortOrder; +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.testbench.unit.UIUnitTest; +import com.vaadin.testbench.unit.ViewPackages; + +@ViewPackages +class GridTesterSortTest extends UIUnitTest { + + SortGridView view; + GridTester, Person> grid_; + GridTester, Person> beanGrid_; + + @BeforeEach + void init() { + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(SortGridView.class); + + view = navigate(SortGridView.class); + grid_ = test(view.grid); + beanGrid_ = test(view.beanGrid); + } + + @Test + void isColumnSortableIndex_getColumnSortableState() { + Assertions.assertTrue(grid_.isColumnSortable(0), + "firstName colum should be sortable"); + Assertions.assertTrue(grid_.isColumnSortable(1), + "age colum should be sortable"); + Assertions.assertFalse(grid_.isColumnSortable(2), + "email colum should not be sortable"); + } + + @Test + void isColumnSortableProperty_getColumnSortableState() { + Assertions.assertTrue(beanGrid_.isColumnSortable("firstName"), + "firstName colum should be sortable"); + Assertions.assertTrue(beanGrid_.isColumnSortable("age"), + "age colum should be sortable"); + Assertions.assertFalse(beanGrid_.isColumnSortable("email"), + "email colum should not be sortable"); + } + + @Test + void isColumnSortable_invalidColumn_throws() { + Assertions.assertThrows(IndexOutOfBoundsException.class, + () -> grid_.isColumnSortable(-1)); + Assertions.assertThrows(IndexOutOfBoundsException.class, + () -> grid_.isColumnSortable(10)); + Assertions.assertThrows(IllegalArgumentException.class, + () -> grid_.isColumnSortable("email")); + Assertions.assertThrows(IllegalArgumentException.class, + () -> beanGrid_.isColumnSortable("notAProperty")); + } + + @Test + void getSortDirectionIndex_sortableColum_getsDirection() { + Assertions.assertNull(grid_.getSortDirection(0)); + + grid_.getComponent().sort(GridSortOrder + .desc(grid_.getComponent().getColumns().get(0)).build()); + Assertions.assertEquals(grid_.getSortDirection(0), + SortDirection.DESCENDING); + + grid_.getComponent().sort(GridSortOrder + .asc(grid_.getComponent().getColumns().get(0)).build()); + Assertions.assertEquals(grid_.getSortDirection(0), + SortDirection.ASCENDING); + } + + @Test + void getSortDirectionOproperty_sortableColum_getsDirection() { + Assertions.assertNull(beanGrid_.getSortDirection("firstName")); + + beanGrid_.getComponent() + .sort(GridSortOrder.desc( + beanGrid_.getComponent().getColumnByKey("firstName")) + .build()); + Assertions.assertEquals(beanGrid_.getSortDirection("firstName"), + SortDirection.DESCENDING); + + beanGrid_.getComponent() + .sort(GridSortOrder.asc( + beanGrid_.getComponent().getColumnByKey("firstName")) + .build()); + Assertions.assertEquals(beanGrid_.getSortDirection("firstName"), + SortDirection.ASCENDING); + } + + @Test + void getSortDirection_nonSortableColum_throws() { + Assertions.assertThrows(IllegalArgumentException.class, + () -> grid_.getSortDirection(2)); + Assertions.assertThrows(IllegalArgumentException.class, + () -> beanGrid_.getSortDirection("email")); + } + + @Test + void getSortDirection_invalidColum_throws() { + Assertions.assertThrows(IndexOutOfBoundsException.class, + () -> grid_.getSortDirection(-1)); + Assertions.assertThrows(IndexOutOfBoundsException.class, + () -> grid_.getSortDirection(10)); + Assertions.assertThrows(IllegalArgumentException.class, + () -> grid_.getSortDirection("email")); + Assertions.assertThrows(IllegalArgumentException.class, + () -> beanGrid_.getSortDirection("notAProperty")); + } + + @Test + void sortByColumnIndex_sortableColumn_gridIsSorted() { + // Sort ASC + grid_.sortByColumn(1); + Assertions.assertIterableEquals( + List.of(view.second, view.third, view.first), + List.of(grid_.getRow(0), grid_.getRow(1), grid_.getRow(2))); + + // Sort DESC + grid_.sortByColumn(1); + Assertions.assertIterableEquals( + List.of(view.first, view.third, view.second), + List.of(grid_.getRow(0), grid_.getRow(1), grid_.getRow(2))); + + // Remove sort + grid_.sortByColumn(1); + Assertions.assertIterableEquals( + List.of(view.first, view.second, view.third), + List.of(grid_.getRow(0), grid_.getRow(1), grid_.getRow(2))); + + } + + @Test + void sortByColumnIndexAndDirection_sortableColumn_gridIsSorted() { + List>> sortEvents = new ArrayList<>(); + view.grid.addSortListener(ev -> sortEvents.add(ev.getSortOrder())); + Grid.Column columnToSort = view.grid.getColumns().get(1); + + // Should click 2 times to get descending order + grid_.sortByColumn(1, SortDirection.DESCENDING); + Assertions.assertEquals(2, sortEvents.size()); + List sorts = sortEvents.stream().flatMap( + s -> s.stream().filter(x -> columnToSort == x.getSorted())) + .map(SortOrder::getDirection).collect(Collectors.toList()); + + Assertions.assertIterableEquals( + List.of(SortDirection.ASCENDING, SortDirection.DESCENDING), + sorts); + + Assertions.assertIterableEquals( + List.of(view.first, view.third, view.second), + List.of(grid_.getRow(0), grid_.getRow(1), grid_.getRow(2))); + + } + + @Test + void sortByColumnProperty_sortableColumn_gridIsSorted() { + beanGrid_.sortByColumn("age"); + Assertions.assertIterableEquals( + List.of(view.second, view.third, view.first), + List.of(beanGrid_.getRow(0), beanGrid_.getRow(1), + beanGrid_.getRow(2))); + // Sort DESC + beanGrid_.sortByColumn("age"); + Assertions.assertIterableEquals( + List.of(view.first, view.third, view.second), + List.of(beanGrid_.getRow(0), beanGrid_.getRow(1), + beanGrid_.getRow(2))); + + // Remove sort + beanGrid_.sortByColumn("age"); + Assertions.assertIterableEquals( + List.of(view.first, view.second, view.third), + List.of(beanGrid_.getRow(0), beanGrid_.getRow(1), + beanGrid_.getRow(2))); + } + + @Test + void sortByColumnPropertyAndDirection_sortableColumn_gridIsSorted() { + List>> sortEvents = new ArrayList<>(); + view.beanGrid.addSortListener(ev -> sortEvents.add(ev.getSortOrder())); + Grid.Column columnToSort = view.beanGrid.getColumnByKey("age"); + + // Should click 2 times to get descending order + beanGrid_.sortByColumn("age", SortDirection.DESCENDING); + Assertions.assertEquals(2, sortEvents.size()); + List sorts = sortEvents.stream().flatMap( + s -> s.stream().filter(x -> columnToSort == x.getSorted())) + .map(SortOrder::getDirection).collect(Collectors.toList()); + + Assertions.assertIterableEquals( + List.of(view.first, view.third, view.second), + List.of(beanGrid_.getRow(0), beanGrid_.getRow(1), + beanGrid_.getRow(2))); + + sortEvents.clear(); + beanGrid_.sortByColumn("age", SortDirection.DESCENDING); + Assertions.assertTrue(sortEvents.isEmpty(), + "Sort direction already reached, should do nothing"); + } + + @Test + void sortByColumn_nonSortableColumn_throws() { + Assertions.assertThrows(IllegalArgumentException.class, + () -> grid_.sortByColumn(2)); + Assertions.assertThrows(IllegalArgumentException.class, + () -> beanGrid_.sortByColumn("email")); + } + + @Test + void sortByColumn_multisort_gridIsSorted() { + view.first.setFirstName("T"); + view.first.setAge(20); + view.second.setFirstName("A"); + view.second.setAge(25); + view.third.setFirstName("G"); + view.third.setAge(25); + view.grid.setMultiSort(true); + + grid_.sortByColumn(0); + grid_.sortByColumn(1, SortDirection.DESCENDING); + Assertions.assertIterableEquals( + List.of(view.second, view.third, view.first), + List.of(grid_.getRow(0), grid_.getRow(1), grid_.getRow(2))); + + } + + @Test + void sortByColumn_multisort_append_gridIsSorted() { + view.grid.setMultiSort(true, Grid.MultiSortPriority.APPEND); + view.first.setFirstName("G"); + view.first.setAge(20); + view.second.setFirstName("G"); + view.second.setAge(25); + view.third.setFirstName("A"); + view.third.setAge(25); + view.grid.setMultiSort(true); + + grid_.sortByColumn(0); + grid_.sortByColumn(1); + Assertions.assertIterableEquals( + List.of(view.third, view.first, view.second), + List.of(grid_.getRow(0), grid_.getRow(1), grid_.getRow(2))); + + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/Person.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/Person.java new file mode 100644 index 000000000..d585a3161 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/Person.java @@ -0,0 +1,184 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.grid; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + * Copied from grid integration-tests. + */ +public class Person implements Serializable, Cloneable { + private int id; + private String firstName; + private String lastName; + private String email; + private int age; + private Address address; + private boolean deceased; + private Date birthDate; + private boolean isSubscriber; + + private Integer salary; // null if unknown + private Double salaryDouble; // null if unknown + + private BigDecimal rent; + + public Person() { + + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + @Override + public String toString() { + return "Person [firstName=" + firstName + ", lastName=" + lastName + + ", email=" + email + ", age=" + age + ", deceased=" + deceased + + ", address=" + address + ", salary=" + salary + + ", salaryDouble=" + salaryDouble + ", rent=" + rent + "]"; + } + + public Person(String firstName, String lastName, String email, int age, + Address address) { + super(); + this.firstName = firstName; + this.lastName = lastName; + this.email = email; + this.age = age; + this.address = address; + } + + public Person(String firstName, int age) { + super(); + this.firstName = firstName; + this.age = age; + } + + public boolean isSubscriber() { + return isSubscriber; + } + + public void setSubscriber(boolean isSubscriber) { + this.isSubscriber = isSubscriber; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public Address getAddress() { + return address; + } + + public void setAddress(Address address) { + this.address = address; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public boolean getDeceased() { + return deceased; + } + + public void setDeceased(boolean deceased) { + this.deceased = deceased; + } + + public Integer getSalary() { + return salary; + } + + public void setSalary(Integer salary) { + this.salary = salary; + } + + public BigDecimal getRent() { + return rent; + } + + public void setRent(BigDecimal rent) { + this.rent = rent; + } + + public Double getSalaryDouble() { + return salaryDouble; + } + + public void setSalaryDouble(Double salaryDouble) { + this.salaryDouble = salaryDouble; + } + + public Date getBirthDate() { + return birthDate; + } + + public void setBirthDate(Date birthDate) { + this.birthDate = birthDate; + } + + @Override + public Person clone() { + try { + return (Person) super.clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeException("The Person object could not be cloned.", + e); + } + } + + public static Person createTestPerson1() { + return new Person("Jorma", "Järvi", "yeah@cool.com", 46, + new Address("Street", 1123, "Turku", Country.FINLAND)); + } + + public static Person createTestPerson2() { + return new Person("Maya", "Dinkelstein", "maya@foo.bar", 18, + new Address("Red street", 12, "Amsterdam", + Country.NETHERLANDS)); + } + + public static Person createTestPerson3() { + return new Person("Richard", "Johnson", "rt@bar.com", 30, + new Address("1st Avenue", 12, "Dallas", Country.USA)); + } + +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/RendererGridView.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/RendererGridView.java new file mode 100644 index 000000000..7615500a6 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/RendererGridView.java @@ -0,0 +1,68 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.grid; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.HasComponents; +import com.vaadin.flow.component.HasText; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.data.renderer.ComponentRenderer; +import com.vaadin.flow.dom.Element; +import com.vaadin.flow.router.Route; + +@Tag("div") +@Route(value = "renderer-grid", registerAtStartup = false) +public class RendererGridView extends Component implements HasComponents { + + Grid grid = new Grid<>(); + Person first = Person.createTestPerson1(); + Person second = Person.createTestPerson2(); + + public RendererGridView() { + grid.setItems(first, second); + + grid.addColumn(new ComponentRenderer<>(p -> new Container( + new Text(p.getFirstName()), new TextNode(p.getLastName()), + new TextNode(Integer.toString(p.getAge()))))) + .setKey("componentRendered"); + grid.addColumn(new ComponentRenderer<>(p -> null)) + .setKey("nullRendered"); + grid.addColumn(new ComponentRenderer<>(p -> new Icon("USER"))); + add(grid); + } + + @Tag("a-container") + public static class Container extends Component implements HasComponents { + public Container(Component... components) { + add(components); + } + } + + @Tag("icon") + public static class Icon extends Component { + public Icon(String name) { + getElement().setProperty("name", name); + } + } + + @Tag("text-node") + public static class TextNode extends Component { + public TextNode(String text) { + super(Element.createText(text)); + } + } + + @Tag("text") + public static class Text extends Component implements HasText { + public Text(String text) { + setText(text); + } + } + +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/SortGridView.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/SortGridView.java new file mode 100644 index 000000000..572dd1bea --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/grid/SortGridView.java @@ -0,0 +1,49 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.grid; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.HasComponents; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.router.Route; + +@Tag("div") +@Route(value = "sort-grid", registerAtStartup = false) +public class SortGridView extends Component implements HasComponents { + + Grid grid = new Grid<>(); + Grid beanGrid = new Grid<>(Person.class); + Person first = Person.createTestPerson1(); + Person second = Person.createTestPerson2(); + Person third = Person.createTestPerson3(); + + final String firstHeader = "Name"; + final String secondHeader = "Age"; + final String thirdHeader = "Email"; + + public SortGridView() { + + grid.setItems(first, second, third); + + grid.addColumn(Person::getFirstName).setHeader(firstHeader) + .setSortable(true); + grid.addColumn(Person::getAge).setHeader(secondHeader) + .setSortable(true); + grid.addColumn(Person::getEmail).setHeader(thirdHeader) + .setSortable(false); + + beanGrid.setItems(first, second, third); + beanGrid.getColumnByKey("firstName"); + beanGrid.getColumnByKey("age"); + beanGrid.getColumnByKey("email").setSortable(false); + + add(grid, beanGrid); + } + +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/html/testbench/AnchorTesterTest.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/html/testbench/AnchorTesterTest.java new file mode 100644 index 000000000..fbfb35241 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/html/testbench/AnchorTesterTest.java @@ -0,0 +1,167 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.html.testbench; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.vaadin.flow.component.UI; +import com.vaadin.flow.component.html.Anchor; +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.flow.server.StreamResource; +import com.vaadin.flow.server.streams.DownloadHandler; +import com.vaadin.testbench.unit.UIUnitTest; +import com.vaadin.testbench.unit.ViewPackages; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +@ViewPackages +class AnchorTesterTest extends UIUnitTest { + @BeforeEach + void init() { + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(AnchorView.class); + } + + @Test + void anchorClick_navigatesCorrectly() { + Anchor anchor = new Anchor("anchor", "Home"); + UI.getCurrent().add(anchor); + + Assertions.assertEquals("anchor", test(anchor).getHref()); + Assertions.assertInstanceOf(AnchorView.class, test(anchor).navigate(), + "Click anchor did not navigate to AnchorView"); + } + + @Test + void anchorClick_navigatesCorrectlyWithParameters() { + Anchor anchor = new Anchor("anchor?name=value", "Home"); + UI.getCurrent().add(anchor); + + Assertions.assertEquals("anchor?name=value", test(anchor).getHref()); + Assertions.assertEquals("anchor", test(anchor).getPath()); + Assertions.assertEquals("name=value", + test(anchor).getQueryParameters().getQueryString()); + Assertions.assertInstanceOf(AnchorView.class, test(anchor).navigate(), + "Click anchor did not navigate to AnchorView"); + } + + @Test + void anchorClick_disabled_throws() { + Anchor anchor = new Anchor("anchor", "Home"); + anchor.setEnabled(false); + UI.getCurrent().add(anchor); + + assertThrows(IllegalStateException.class, () -> test(anchor).click()); + } + + @Test + void anchorClick_notARegisteredRoute_throws() { + Anchor anchor = new Anchor("error", "Home"); + UI.getCurrent().add(anchor); + + final IllegalStateException exception = assertThrows( + IllegalStateException.class, () -> test(anchor).click()); + + Assertions.assertEquals("Anchor is not for an application route", + exception.getMessage()); + } + + @Test + void anchorClick_streamRegistration_throws() { + StreamResource resource = new StreamResource("filename", + () -> new ByteArrayInputStream( + "Hello world".getBytes(StandardCharsets.UTF_8))); + Anchor anchor = new Anchor(resource, "Home"); + UI.getCurrent().add(anchor); + + final IllegalStateException exception = assertThrows( + IllegalStateException.class, () -> test(anchor).click()); + + Assertions.assertEquals("Anchor target seems to be a resource", + exception.getMessage()); + } + + @Test + void anchorDownload_writesResourceToOutputStream() { + StreamResource resource = new StreamResource("filename", + () -> new ByteArrayInputStream( + "Hello world".getBytes(StandardCharsets.UTF_8))); + + Anchor anchor = new Anchor(resource, "Download"); + UI.getCurrent().add(anchor); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + test(anchor).download(outputStream); + + Assertions.assertEquals("Hello world", + outputStream.toString(StandardCharsets.UTF_8)); + } + + @Test + void anchorDownload_disabled_throws() { + DownloadHandler resource = event -> event.getOutputStream() + .write("Hello world".getBytes(StandardCharsets.UTF_8)); + + Anchor anchor = new Anchor(resource, "Download"); + anchor.setEnabled(false); + UI.getCurrent().add(anchor); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + assertThrows(IllegalStateException.class, + () -> test(anchor).download(outputStream)); + } + + @Test + void anchorClick_downloadHandler_throws() { + DownloadHandler resource = event -> event.getOutputStream() + .write("Hello world".getBytes(StandardCharsets.UTF_8)); + Anchor anchor = new Anchor(resource, "Home"); + UI.getCurrent().add(anchor); + + final IllegalStateException exception = assertThrows( + IllegalStateException.class, () -> test(anchor).click()); + + Assertions.assertEquals("Anchor target seems to be a resource", + exception.getMessage()); + } + + @Test + void anchorDownload_downloadHandler_writesResourceToOutputStream() { + DownloadHandler resource = event -> event.getOutputStream() + .write("Hello world".getBytes(StandardCharsets.UTF_8)); + Anchor anchor = new Anchor(resource, "Download"); + UI.getCurrent().add(anchor); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + test(anchor).download(outputStream); + + Assertions.assertEquals("Hello world", + outputStream.toString(StandardCharsets.UTF_8)); + } + + @Test + void anchorDownload_noStreamRegistration_throws() { + Anchor anchor = new Anchor("anchor", "Download"); + UI.getCurrent().add(anchor); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + final IllegalStateException exception = assertThrows( + IllegalStateException.class, + () -> test(anchor).download(outputStream)); + + Assertions.assertEquals("Anchor target does not seem to be a resource", + exception.getMessage()); + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/html/testbench/AnchorView.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/html/testbench/AnchorView.java new file mode 100644 index 000000000..632c1b9df --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/html/testbench/AnchorView.java @@ -0,0 +1,19 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.html.testbench; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.router.Route; + +@Tag("a") +@Route(value = "anchor", registerAtStartup = false) +public class AnchorView extends Component { + +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/html/testbench/RangeInputTesterTest.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/html/testbench/RangeInputTesterTest.java new file mode 100644 index 000000000..b97b193dc --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/html/testbench/RangeInputTesterTest.java @@ -0,0 +1,204 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.html.testbench; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.vaadin.flow.component.html.RangeInput; +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.testbench.unit.UIUnitTest; +import com.vaadin.testbench.unit.ViewPackages; + +@ViewPackages +class RangeInputTesterTest extends UIUnitTest { + + RangeInputTester tester; + RangeInput component; + + @BeforeEach + void init() { + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(RangeInputView.class); + component = navigate(RangeInputView.class).rangeInput; + tester = test(component); + } + + @Test + void setValue_unusable_throws() { + component.setEnabled(false); + Assertions.assertThrows(IllegalStateException.class, + () -> tester.setValue(12.3)); + + component.setEnabled(true); + component.setVisible(false); + Assertions.assertThrows(IllegalStateException.class, + () -> tester.setValue(12.3)); + + } + + @Test + void setValue_allowedValue_valueSet() { + double newValue = 12.0; + tester.setValue(newValue); + Assertions.assertEquals(newValue, component.getValue()); + + newValue = component.getMin(); + tester.setValue(newValue); + Assertions.assertEquals(newValue, component.getValue()); + + newValue = component.getMax(); + tester.setValue(newValue); + Assertions.assertEquals(newValue, component.getValue()); + } + + @Test + void setValue_null_throws() { + Assertions.assertThrows(IllegalArgumentException.class, + () -> tester.setValue(null)); + } + + @Test + void setValue_valueNotRespectingStep_throws() { + Assertions.assertThrows(IllegalArgumentException.class, + () -> tester.setValue(12.3), + "Accepted value not respecting step"); + component.setValue(4.0); + Assertions.assertEquals(4, 0, component.getValue()); + component.setValue(3.0); + Assertions.assertEquals(3.0, component.getValue()); + } + + @Test + void setValue_customStep() { + double step = 3.3; + double initialValue = 1.3; + component.setStep(step); + component.setValue(initialValue); + + double newValue = initialValue + (step * 2); + tester.setValue(newValue); + Assertions.assertEquals(newValue, component.getValue()); + + Assertions.assertThrows(IllegalArgumentException.class, + () -> tester.setValue(newValue + 1), + "Accepted value not matching step"); + } + + @Test + void setValue_undefinedStep_valueAccepted() { + component.setStep(null); + double newValue = 12.3; + tester.setValue(newValue); + Assertions.assertEquals(newValue, component.getValue()); + } + + @Test + void setValue_lessThanMin_throws() { + Assertions.assertThrows(IllegalArgumentException.class, + () -> tester.setValue(-100.0), "Accepted value less than min"); + } + + @Test + void setValue_greaterThanMax_throws() { + Assertions.assertThrows(IllegalArgumentException.class, + () -> tester.setValue(100.0), + "Accepted value greater than max"); + } + + @Test + void getValue_unusable_getsValue() { + double newValue = 12.4; + component.setValue(newValue); + Assertions.assertEquals(newValue, component.getValue()); + } + + @Test + void getValue_hidden_throws() { + component.setVisible(false); + Assertions.assertThrows(IllegalStateException.class, + () -> tester.getValue()); + } + + @Test + void increase_addStepToValue() { + tester.increase(); + Assertions.assertEquals(1.0, component.getValue()); + tester.increase(); + Assertions.assertEquals(2.0, component.getValue()); + tester.increase(5); + Assertions.assertEquals(7.0, component.getValue()); + + } + + @Test + void increase_negativeTimes_throws() { + Assertions.assertThrows(IllegalArgumentException.class, + () -> tester.increase(-1)); + } + + @Test + void increase_greaterThanMax_throws() { + Assertions.assertThrows(IllegalArgumentException.class, + () -> tester.increase(30)); + } + + @Test + void increase_undefinedStep_throws() { + component.setStep(null); + Assertions.assertThrows(IllegalStateException.class, + () -> tester.increase()); + } + + @Test + void increase_unusable_throws() { + component.setEnabled(false); + Assertions.assertThrows(IllegalStateException.class, + () -> tester.increase()); + } + + @Test + void decrease_addStepToValue() { + tester.decrease(); + Assertions.assertEquals(-1.0, component.getValue()); + tester.decrease(); + Assertions.assertEquals(-2.0, component.getValue()); + tester.decrease(5); + Assertions.assertEquals(-7.0, component.getValue()); + + } + + @Test + void decrease_lessThanMin_throws() { + Assertions.assertThrows(IllegalArgumentException.class, + () -> tester.decrease(30)); + } + + @Test + void decrease_undefinedStep_throws() { + component.setStep(null); + Assertions.assertThrows(IllegalStateException.class, + () -> tester.decrease()); + } + + @Test + void decrease_unusable_throws() { + component.setEnabled(false); + Assertions.assertThrows(IllegalStateException.class, + () -> tester.decrease()); + } + + @Test + void decrease_negativeTimes_throws() { + Assertions.assertThrows(IllegalArgumentException.class, + () -> tester.decrease(-1)); + } + +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/html/testbench/RangeInputView.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/html/testbench/RangeInputView.java new file mode 100644 index 000000000..053db6aef --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/html/testbench/RangeInputView.java @@ -0,0 +1,29 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.html.testbench; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.HasComponents; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.component.html.RangeInput; +import com.vaadin.flow.router.Route; + +@Tag("div") +@Route(value = "range-input", registerAtStartup = false) +public class RangeInputView extends Component implements HasComponents { + + RangeInput rangeInput = new RangeInput(); + + public RangeInputView() { + rangeInput.setMin(-20); + rangeInput.setMax(20); + add(rangeInput); + } + +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/listbox/ListBoxTesterTest.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/listbox/ListBoxTesterTest.java new file mode 100644 index 000000000..040ab0116 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/listbox/ListBoxTesterTest.java @@ -0,0 +1,58 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.listbox; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.testbench.unit.UIUnitTest; +import com.vaadin.testbench.unit.ViewPackages; + +import static org.junit.jupiter.api.Assertions.assertIterableEquals; + +@ViewPackages +class ListBoxTesterTest extends UIUnitTest { + ListBoxView view; + + @BeforeEach + public void registerView() { + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(ListBoxView.class); + view = navigate(ListBoxView.class); + } + + @Test + void getSuggestionItems_returnsAllItems() { + assertIterableEquals(view.selection, + test(view.listBox).getSuggestionItems()); + } + + @Test + void stringSelect_getSuggestions_valuesEqualItems() { + assertIterableEquals(view.selection, + test(view.listBox).getSuggestions()); + } + + @Test + void stringSelect_selectItem_selectsCorrectItem() { + Assertions.assertNull(test(view.listBox).getSelected()); + + test(view.listBox).selectItem("two"); + + Assertions.assertSame(view.selection.get(1), + test(view.listBox).getSelected()); + + test(view.listBox).selectItem(null); + + Assertions.assertNull(test(view.listBox).getSelected(), + "Selecting null should clear selection"); + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/listbox/ListBoxView.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/listbox/ListBoxView.java new file mode 100644 index 000000000..e27f55fa3 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/listbox/ListBoxView.java @@ -0,0 +1,36 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.listbox; + +import java.util.Arrays; +import java.util.List; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.HasComponents; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.router.Route; + +@Tag("div") +@Route(value = "list-boxes", registerAtStartup = false) +public class ListBoxView extends Component implements HasComponents { + ListBox listBox; + MultiSelectListBox multiSelectListBox; + + List selection = Arrays.asList("one", "two"); + + public ListBoxView() { + listBox = new ListBox<>(); + listBox.setItems(selection); + + multiSelectListBox = new MultiSelectListBox<>(); + multiSelectListBox.setItems(selection); + + add(listBox, multiSelectListBox); + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/listbox/MultiSelectListBoxTesterTest.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/listbox/MultiSelectListBoxTesterTest.java new file mode 100644 index 000000000..8e7a7c4d1 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/listbox/MultiSelectListBoxTesterTest.java @@ -0,0 +1,92 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.listbox; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.testbench.unit.UIUnitTest; +import com.vaadin.testbench.unit.ViewPackages; + +import static org.junit.jupiter.api.Assertions.assertIterableEquals; + +@ViewPackages +class MultiSelectListBoxTesterTest extends UIUnitTest { + ListBoxView view; + + @BeforeEach + public void registerView() { + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(ListBoxView.class); + view = navigate(ListBoxView.class); + } + + @Test + void getSuggestionItems_returnsAllItems() { + assertIterableEquals(view.selection, + test(view.multiSelectListBox).getSuggestionItems()); + } + + @Test + void stringSelect_getSuggestions_valuesEqualItems() { + assertIterableEquals(view.selection, + test(view.multiSelectListBox).getSuggestions()); + } + + @Test + void stringSelect_selectItemsDeselectItems_selectsCorrectItem() { + final MultiSelectListBoxTester, String> list_ = test( + view.multiSelectListBox); + Assertions.assertTrue(list_.getSelected().isEmpty()); + + list_.selectItems("two"); + + Assertions.assertIterableEquals(view.selection.subList(1, 2), + list_.getSelected()); + + list_.deselectItems("two"); + + Assertions.assertTrue(list_.getSelected().isEmpty(), + "Deselecting item should clear selection"); + } + + @Test + void stringSelect_selectItems_addsToSelection() { + final MultiSelectListBoxTester, String> list_ = test( + view.multiSelectListBox); + Assertions.assertTrue(list_.getSelected().isEmpty()); + + list_.selectItems("two"); + + Assertions.assertIterableEquals(view.selection.subList(1, 2), + list_.getSelected()); + + list_.selectItems("one"); + + Assertions.assertIterableEquals(view.selection, list_.getSelected()); + } + + @Test + void clearSelection_selectItems_addsToSelection() { + final MultiSelectListBoxTester, String> list_ = test( + view.multiSelectListBox); + Assertions.assertTrue(list_.getSelected().isEmpty()); + + list_.selectItems("two", "one"); + + Assertions.assertIterableEquals(view.selection, list_.getSelected()); + + list_.clearSelection(); + + Assertions.assertTrue(list_.getSelected().isEmpty()); + } + +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/login/LoginFormTesterTest.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/login/LoginFormTesterTest.java new file mode 100644 index 000000000..847e8bf0d --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/login/LoginFormTesterTest.java @@ -0,0 +1,71 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.login; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.vaadin.flow.component.html.Span; +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.testbench.unit.UIUnitTest; +import com.vaadin.testbench.unit.ViewPackages; + +@ViewPackages +public class LoginFormTesterTest extends UIUnitTest { + + LoginFormView view; + + @BeforeEach + void init() { + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(LoginFormView.class); + view = navigate(LoginFormView.class); + } + + @Test + void login_generatesLoginEvent() { + test(view.login).login("user", "pwd"); + Assertions.assertEquals(1, $(Span.class).from(view).all().size()); + Span message = $(Span.class).from(view).withId("m1").first(); + Assertions.assertEquals(view.generateLoginMessage("user", "pwd"), + message.getText()); + } + + @Test + void login_disablesLoginComponent() { + test(view.login).login("admin", "adm"); + + Assertions.assertFalse(test(view.login).isUsable(), + "Login should be disabled after a login event."); + + Assertions.assertThrows(IllegalStateException.class, + () -> test(view.login).login("us", "er"), + "Disabled login should not accept login event"); + } + + @Test + void forgotPassword_generatesEvent() { + test(view.login).forgotPassword(); + + Assertions.assertEquals(1, $(Span.class).from(view).all().size()); + Span message = $(Span.class).from(view).withId("m1").first(); + Assertions.assertEquals("forgot", message.getText()); + } + + @Test + void forgotButtonHidden_usingForgotThrows() { + view.login.setForgotPasswordButtonVisible(false); + + Assertions.assertThrows(IllegalStateException.class, + () -> test(view.login).forgotPassword(), + "Hidden forgot password button should not be usable."); + } + +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/login/LoginFormView.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/login/LoginFormView.java new file mode 100644 index 000000000..97bb7e3a3 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/login/LoginFormView.java @@ -0,0 +1,44 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.login; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.HasComponents; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.component.Text; +import com.vaadin.flow.component.html.Div; +import com.vaadin.flow.component.html.Span; +import com.vaadin.flow.router.Route; + +@Tag("div") +@Route(value = "login-view", registerAtStartup = false) +public class LoginFormView extends Component implements HasComponents { + LoginForm login; + Div messages; + + public LoginFormView() { + this.login = new LoginForm(); + messages = new Div(new Text("Messages")); + login.addLoginListener(loginEvent -> addMessage(generateLoginMessage( + loginEvent.getUsername(), loginEvent.getPassword()))); + login.addForgotPasswordListener( + forgotPasswordEvent -> addMessage("forgot")); + add(login, messages); + } + + protected static String generateLoginMessage(String user, String password) { + return "login: " + user + " :: " + password; + } + + protected void addMessage(String message) { + Span messageElement = new Span(message); + messageElement.setId("m" + messages.getChildren().count()); + messages.add(messageElement); + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/login/LoginOverlayTesterTest.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/login/LoginOverlayTesterTest.java new file mode 100644 index 000000000..5c4412ebf --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/login/LoginOverlayTesterTest.java @@ -0,0 +1,84 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.login; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.vaadin.flow.component.html.Span; +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.testbench.unit.UIUnitTest; +import com.vaadin.testbench.unit.ViewPackages; + +@ViewPackages +public class LoginOverlayTesterTest extends UIUnitTest { + + LoginOverlayView view; + LoginOverlayTester login_; + + @BeforeEach + void init() { + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(LoginOverlayView.class); + view = navigate(LoginOverlayView.class); + login_ = test(view.login); + } + + @Test + void overlayClosed_throwsException() { + Assertions.assertThrows(IllegalStateException.class, + () -> login_.login("user", "user")); + } + + @Test + void login_generatesLoginEvent() { + login_.openOverlay(); + + login_.login("user", "pwd"); + Assertions.assertEquals(1, $(Span.class).from(view).all().size()); + Span message = $(Span.class).from(view).withId("m1").first(); + Assertions.assertEquals(view.generateLoginMessage("user", "pwd"), + message.getText()); + } + + @Test + void login_disablesLoginComponent() { + login_.openOverlay(); + login_.login("admin", "adm"); + + Assertions.assertFalse(login_.isUsable(), + "Login should be disabled after a login event."); + + Assertions.assertThrows(IllegalStateException.class, + () -> login_.login("us", "er"), + "Disabled login should not accept login event"); + } + + @Test + void forgotPassword_generatesEvent() { + login_.openOverlay(); + login_.forgotPassword(); + + Assertions.assertEquals(1, $(Span.class).from(view).all().size()); + Span message = $(Span.class).from(view).withId("m1").first(); + Assertions.assertEquals("forgot", message.getText()); + } + + @Test + void forgotButtonHidden_usingForgotThrows() { + login_.openOverlay(); + view.login.setForgotPasswordButtonVisible(false); + + Assertions.assertThrows(IllegalStateException.class, + () -> login_.forgotPassword(), + "Hidden forgot password button should not be usable."); + } + +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/login/LoginOverlayView.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/login/LoginOverlayView.java new file mode 100644 index 000000000..b0500c318 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/login/LoginOverlayView.java @@ -0,0 +1,47 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.login; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.HasComponents; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.component.Text; +import com.vaadin.flow.component.button.Button; +import com.vaadin.flow.component.html.Div; +import com.vaadin.flow.component.html.Span; +import com.vaadin.flow.router.Route; + +@Tag("div") +@Route(value = "login-view", registerAtStartup = false) +public class LoginOverlayView extends Component implements HasComponents { + LoginOverlay login; + Div messages; + + public LoginOverlayView() { + this.login = new LoginOverlay(); + messages = new Div(new Text("Messages")); + login.addLoginListener(loginEvent -> addMessage(generateLoginMessage( + loginEvent.getUsername(), loginEvent.getPassword()))); + login.addForgotPasswordListener( + forgotPasswordEvent -> addMessage("forgot")); + + Button open = new Button("Login", event -> login.setOpened(true)); + add(open, login, messages); + } + + protected static String generateLoginMessage(String user, String password) { + return "login: " + user + " :: " + password; + } + + protected void addMessage(String message) { + Span messageElement = new Span(message); + messageElement.setId("m" + messages.getChildren().count()); + messages.add(messageElement); + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/menubar/MenuBarTesterTest.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/menubar/MenuBarTesterTest.java new file mode 100644 index 000000000..49e073fa6 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/menubar/MenuBarTesterTest.java @@ -0,0 +1,260 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.menubar; + +import java.util.List; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.testbench.unit.UIUnitTest; +import com.vaadin.testbench.unit.ViewPackages; + +@ViewPackages +class MenuBarTesterTest extends UIUnitTest { + + MenuBarView view; + MenuBarTester menu_; + + @BeforeEach + void init() { + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(MenuBarView.class); + view = navigate(MenuBarView.class); + menu_ = test(view.menu); + } + + @Test + void clickItem_byText_actionExecuted() { + menu_.clickItem("Foo"); + Assertions.assertIterableEquals(List.of("Foo"), view.clickedItems); + + menu_.clickItem("Text"); + menu_.clickItem("Bar"); + Assertions.assertIterableEquals(List.of("Foo", "Text", "Bar"), + view.clickedItems); + } + + @Test + void clickItem_notExisting_throws() { + Assertions.assertThrows(IllegalArgumentException.class, + () -> menu_.clickItem("XYZ")); + } + + @Test + void clickItem_menuNotUsable_throws() { + view.menu.setEnabled(false); + Assertions.assertThrows(IllegalStateException.class, + () -> menu_.clickItem("Bar")); + + view.menu.setEnabled(true); + view.menu.setVisible(false); + Assertions.assertThrows(IllegalStateException.class, + () -> menu_.clickItem("Bar")); + } + + @Test + void clickItem_multipleMatches_throws() { + Assertions.assertThrows(IllegalStateException.class, + () -> menu_.clickItem("Duplicated")); + } + + @Test + void clickItem_checkable_checkStatusChanges() { + menu_.clickItem("Checkables", "Checkable"); + Assertions.assertIterableEquals(List.of("Checkable"), + view.clickedItems); + Assertions.assertTrue(view.checkableItem.isChecked(), + "Item should be checked but was not"); + + menu_.clickItem("Checkables", "Checkable"); + Assertions.assertIterableEquals(List.of("Checkable", "Checkable"), + view.clickedItems); + Assertions.assertFalse(view.checkableItem.isChecked(), + "Item should not be checked but was"); + } + + @Test + void clickItem_disabled_throws() { + Assertions.assertThrows(IllegalStateException.class, + () -> menu_.clickItem("Disabled")); + Assertions.assertTrue(view.clickedItems.isEmpty(), + "Listener should not have been notified"); + } + + @Test + void clickItem_hidden_throws() { + Assertions.assertThrows(IllegalStateException.class, + () -> menu_.clickItem("Hidden")); + Assertions.assertTrue(view.clickedItems.isEmpty(), + "Listener should not have been notified"); + } + + @Test + void clickItem_nested_executeAction() { + menu_.clickItem("Hierarchical", "Level2"); + Assertions.assertIterableEquals(List.of("Hierarchical / Level2"), + view.clickedItems); + + view.clickedItems.clear(); + menu_.clickItem("Hierarchical", "NestedSubMenu", "Level3"); + Assertions.assertIterableEquals( + List.of("Hierarchical / NestedSubMenu / Level3"), + view.clickedItems); + } + + @Test + void clickItem_nestedWrongPath_throws() { + Assertions.assertThrows(IllegalArgumentException.class, + () -> menu_.clickItem("Foo", "Bar")); + Assertions.assertThrows(IllegalArgumentException.class, + () -> menu_.clickItem("Hierarchical", "Level3")); + Assertions.assertThrows(IllegalArgumentException.class, () -> menu_ + .clickItem("Hierarchical", "NestedSubMenu", "Level3Bis")); + Assertions.assertThrows(IllegalArgumentException.class, + () -> menu_.clickItem("Hierarchical", "NestedSubMenu", "Level3", + "Level4")); + } + + @Test + void clickItem_nestedNotUsableParent_throws() { + Assertions.assertThrows(IllegalStateException.class, () -> menu_ + .clickItem("Hierarchical", "NestedDisabled", "Level3")); + Assertions.assertThrows(IllegalStateException.class, () -> menu_ + .clickItem("Hierarchical", "NestedInvisible", "Level3")); + + } + + @Test + void clickItem_byIndex_executesAction() { + menu_.clickItem(0); + Assertions.assertIterableEquals(List.of("Foo"), view.clickedItems); + + menu_.clickItem(2); + menu_.clickItem(1); + Assertions.assertIterableEquals(List.of("Foo", "Text", "Bar"), + view.clickedItems); + + } + + @Test + void clickItem_byInvalidIndexes_throws() { + Assertions.assertThrows(IllegalArgumentException.class, + () -> menu_.clickItem(1, 2)); + Assertions.assertThrows(IllegalArgumentException.class, + () -> menu_.clickItem(7, 5)); + Assertions.assertThrows(IllegalArgumentException.class, + () -> menu_.clickItem(7, 1, 5)); + Assertions.assertThrows(IllegalArgumentException.class, + () -> menu_.clickItem(7, 1, 0, 4)); + } + + @Test + void clickItem_byNestedIndexNotUsableParent_throws() { + // Hierarchical / NestedDisabled / Level3 + Assertions.assertThrows(IllegalStateException.class, + () -> menu_.clickItem(7, 3, 0)); + } + + @Test + void clickItem_byNestedIndexes_executesAction() { + menu_.clickItem(7, 0); + Assertions.assertIterableEquals(List.of("Hierarchical / Level2"), + view.clickedItems); + + view.clickedItems.clear(); + menu_.clickItem(7, 1, 0); + Assertions.assertIterableEquals( + List.of("Hierarchical / NestedSubMenu / Level3"), + view.clickedItems); + + } + + @Test + void isItemChecked_byText_getCheckedStatus() { + Assertions.assertFalse(menu_.isItemChecked("Checkables", "Checkable"), + "Checkable item should not be checked by default, but result is true"); + + view.checkableItem.setChecked(true); + Assertions.assertTrue(menu_.isItemChecked("Checkables", "Checkable"), + "Checkable item is checked, but result is false"); + + view.checkableItem.setChecked(false); + Assertions.assertFalse(menu_.isItemChecked("Checkables", "Checkable"), + "Checkable item is not checked, but result is true"); + } + + @Test + void isItemChecked_byIndex_getCheckedStatus() { + Assertions.assertFalse(menu_.isItemChecked(5, 0), + "Checkable item should not be checked by default, but result is true"); + + view.checkableItem.setChecked(true); + Assertions.assertTrue(menu_.isItemChecked(5, 0), + "Checkable item is checked, but result is false"); + + view.checkableItem.setChecked(false); + Assertions.assertFalse(menu_.isItemChecked(5, 0), + "Checkable item is not checked, but result is true"); + } + + @Test + void isItemChecked_nestedByText_getCheckedStatus() { + Assertions.assertTrue( + menu_.isItemChecked("Hierarchical", "Nested Checkable"), + "Checkable item should be checked by default, but result is false"); + + view.nestedCheckableItem.setChecked(false); + Assertions.assertFalse( + menu_.isItemChecked("Hierarchical", "Nested Checkable"), + "Checkable item is not checked, but result is true"); + + view.nestedCheckableItem.setChecked(true); + Assertions.assertTrue( + menu_.isItemChecked("Hierarchical", "Nested Checkable"), + "Checkable item is checked, but result is false"); + } + + @Test + void isItemChecked_nestedByIndex_getCheckedStatus() { + Assertions.assertTrue(menu_.isItemChecked(7, 2), + "Checkable item should be checked by default, but result is false"); + + view.nestedCheckableItem.setChecked(false); + Assertions.assertFalse(menu_.isItemChecked(7, 2), + "Checkable item is not checked, but result is true"); + + view.nestedCheckableItem.setChecked(true); + Assertions.assertTrue(menu_.isItemChecked(7, 2), + "Checkable item is checked, but result is false"); + } + + @Test + void isItemChecked_notCheckableItem_throws() { + Assertions.assertThrows(IllegalArgumentException.class, + () -> menu_.isItemChecked("Bar")); + Assertions.assertThrows(IllegalArgumentException.class, + () -> menu_.isItemChecked("Hierarchical", "Level2")); + Assertions.assertThrows(IllegalArgumentException.class, + () -> menu_.isItemChecked(0)); + Assertions.assertThrows(IllegalArgumentException.class, + () -> menu_.isItemChecked(7, 1)); + } + + @Test + void isItemChecked_notExisting_throws() { + Assertions.assertThrows(IllegalArgumentException.class, + () -> menu_.isItemChecked("XYZ")); + Assertions.assertThrows(IllegalArgumentException.class, + () -> menu_.isItemChecked(22)); + } + +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/menubar/MenuBarView.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/menubar/MenuBarView.java new file mode 100644 index 000000000..2fe7b0f22 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/menubar/MenuBarView.java @@ -0,0 +1,72 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.menubar; + +import java.util.ArrayList; +import java.util.List; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.HasComponents; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.component.contextmenu.MenuItem; +import com.vaadin.flow.component.contextmenu.SubMenu; +import com.vaadin.flow.router.Route; + +@Tag("div") +@Route(value = "contextmenu", registerAtStartup = false) +public class MenuBarView extends Component implements HasComponents { + + final MenuItem checkableItem; + final MenuItem nestedCheckableItem; + final List clickedItems = new ArrayList<>(); + final MenuBar menu; + + public MenuBarView() { + Span assignee = new Span(); + menu = new MenuBar(); + menu.addItem("Foo", ev -> clickedItems.add("Foo")); + menu.addItem("Bar", ev -> clickedItems.add("Bar")); + menu.addItem("Text", ev -> clickedItems.add("Text")); + menu.addItem("Duplicated", ev -> clickedItems.add("Duplicated 1")); + menu.addItem("Duplicated", ev -> clickedItems.add("Duplicated 2")); + SubMenu checkablesSubMenu = menu.addItem("Checkables").getSubMenu(); + checkableItem = checkablesSubMenu.addItem("Checkable", + ev -> clickedItems.add("Checkable")); + checkableItem.setCheckable(true); + menu.addItem("Disabled", ev -> clickedItems.add("Disabled")) + .setEnabled(false); + menu.addItem("Hidden", ev -> clickedItems.add("Hidden")) + .setVisible(false); + + SubMenu subMenu = menu.addItem("Hierarchical").getSubMenu(); + subMenu.addItem("Level2", + ev -> clickedItems.add("Hierarchical / Level2")); + subMenu.addItem("NestedSubMenu").getSubMenu().addItem("Level3", + ev -> clickedItems + .add("Hierarchical / NestedSubMenu / Level3")); + nestedCheckableItem = subMenu.addItem("Nested Checkable"); + nestedCheckableItem.setCheckable(true); + nestedCheckableItem.setChecked(true); + MenuItem nestedDisabled = subMenu.addItem("NestedDisabled"); + nestedDisabled.setEnabled(false); + nestedDisabled.getSubMenu().addItem("Level3", ev -> clickedItems + .add("Hierarchical / NestedDisabled / Level3")); + MenuItem nestedInvisible = subMenu.addItem("NestedInvisible"); + nestedInvisible.setVisible(false); + nestedInvisible.getSubMenu().addItem("Level3", ev -> clickedItems + .add("Hierarchical / NestedInvisible / Level3")); + + add(menu); + } + + @Tag("span") + private static class Span extends Component { + + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/messages/MessageInputTesterTest.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/messages/MessageInputTesterTest.java new file mode 100644 index 000000000..741ca8ae8 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/messages/MessageInputTesterTest.java @@ -0,0 +1,69 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.messages; + +import java.util.concurrent.atomic.AtomicReference; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.testbench.unit.UIUnitTest; +import com.vaadin.testbench.unit.ViewPackages; + +@ViewPackages +class MessageInputTesterTest extends UIUnitTest { + + MessagesView view; + + @BeforeEach + void init() { + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(MessagesView.class); + view = navigate(MessagesView.class); + } + + @Test + void emptyMessage_noEventIsFired() { + AtomicReference message = new AtomicReference<>(); + + view.input.addSubmitListener(submitEvent -> message.compareAndSet(null, + submitEvent.getValue())); + + final String testMessage = ""; + test(view.input).send(testMessage); + Assertions.assertNull(message.get()); + } + + @Test + void sendMessage_eventIsReceived() { + AtomicReference message = new AtomicReference<>(); + + view.input.addSubmitListener(submitEvent -> message.compareAndSet(null, + submitEvent.getValue())); + + final String testMessage = "Hello"; + test(view.input).send(testMessage); + Assertions.assertEquals(testMessage, message.get()); + } + + @Test + void disabledMessage_throwsException() { + AtomicReference message = new AtomicReference<>(); + + view.input.addSubmitListener(submitEvent -> message.compareAndSet(null, + submitEvent.getValue())); + view.input.setEnabled(false); + + final String testMessage = "Hello"; + Assertions.assertThrows(IllegalStateException.class, + () -> test(view.input).send(testMessage)); + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/messages/MessageListTesterTest.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/messages/MessageListTesterTest.java new file mode 100644 index 000000000..f53acec51 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/messages/MessageListTesterTest.java @@ -0,0 +1,126 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.messages; + +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.ArrayList; +import java.util.Arrays; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.testbench.unit.TreeOnFailureExtension; +import com.vaadin.testbench.unit.UIUnitTest; +import com.vaadin.testbench.unit.ViewPackages; + +@ViewPackages +@ExtendWith(TreeOnFailureExtension.class) +class MessageListTesterTest extends UIUnitTest { + + MessagesView view; + + @BeforeEach + void init() { + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(MessagesView.class); + view = navigate(MessagesView.class); + } + + @Test + void size_returnsCorrectSize() { + Assertions.assertEquals(3, test(view.list).size()); + + addItem(view.list, new MessageListItem("Added message")); + + Assertions.assertEquals(4, test(view.list).size(), + "Message should have been added to the list"); + } + + @Test + void getMessages_allMessagesReturned() { + Assertions.assertIterableEquals( + Arrays.asList(view.one, view.two, view.three), + test(view.list).getMessages()); + } + + @Test + void getMessageByIndex_correctMessageReturned() { + final MessageListTester list_ = test(view.list); + Assertions.assertEquals(view.two, list_.getMessage(1)); + Assertions.assertEquals(view.three, list_.getMessage(2)); + Assertions.assertEquals(view.one, list_.getMessage(0)); + } + + @Test + void getMessageFromTime_messagesAfterAreReturned() { + final MessageListTester list_ = test(view.list); + Assertions.assertIterableEquals(Arrays.asList(view.two, view.three), + list_.getMessagesAfter(LocalDateTime.now().withHour(11) + .toInstant(ZoneOffset.UTC))); + Assertions.assertIterableEquals(Arrays.asList(view.three), + list_.getMessagesAfter(LocalDateTime.now().withHour(12) + .toInstant(ZoneOffset.UTC))); + } + + @Test + void getMessageUpToTime_messagesBeforeAreReturned() { + final MessageListTester list_ = test(view.list); + Assertions.assertIterableEquals(Arrays.asList(view.one, view.two), + list_.getMessagesBefore(LocalDateTime.now().withHour(13) + .toInstant(ZoneOffset.UTC))); + Assertions.assertIterableEquals(Arrays.asList(view.one), + list_.getMessagesBefore(LocalDateTime.now().withHour(11) + .toInstant(ZoneOffset.UTC))); + } + + @Test + void getMessageBetweenTime_matchingMessagesReturned() { + Assertions.assertIterableEquals(Arrays.asList(view.two), + test(view.list).getMessages( + LocalDateTime.now().withHour(1) + .toInstant(ZoneOffset.UTC), + LocalDateTime.now().withHour(13) + .toInstant(ZoneOffset.UTC))); + } + + @Test + void getMessageForUser_messagesForUserReturned() { + final MessageListTester list_ = test(view.list); + Assertions.assertIterableEquals(Arrays.asList(view.one, view.three), + list_.getMessages("Joe")); + Assertions.assertIterableEquals(Arrays.asList(view.two), + list_.getMessages("Jane")); + + Assertions.assertTrue(list_.getMessages(null).isEmpty()); + + final MessageListItem nullUser = new MessageListItem("hi"); + addItem(view.list, nullUser); + + Assertions.assertIterableEquals(Arrays.asList(nullUser), + list_.getMessages(null)); + } + + /** + * Add a new message item to the MessageList. + * + * @param item + * item to add + */ + private void addItem(MessageList list, MessageListItem item) { + + final ArrayList messageListItems = new ArrayList<>( + list.getItems()); + messageListItems.add(item); + list.setItems(messageListItems); + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/messages/MessagesView.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/messages/MessagesView.java new file mode 100644 index 000000000..bb87a19db --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/messages/MessagesView.java @@ -0,0 +1,40 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.messages; + +import java.time.LocalDateTime; +import java.time.ZoneOffset; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.HasComponents; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.router.Route; + +@Tag("div") +@Route(value = "messages", registerAtStartup = false) +public class MessagesView extends Component implements HasComponents { + + MessageInput input; + MessageList list; + + MessageListItem one = new MessageListItem("one", + LocalDateTime.now().withHour(0).toInstant(ZoneOffset.UTC), "Joe"); + MessageListItem two = new MessageListItem("two", + LocalDateTime.now().withHour(12).toInstant(ZoneOffset.UTC), "Jane"); + MessageListItem three = new MessageListItem("three", + LocalDateTime.now().withHour(23).toInstant(ZoneOffset.UTC), "Joe"); + + public MessagesView() { + list = new MessageList(); + input = new MessageInput(); + list.setItems(one, two, three); + + add(input, list); + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/notification/NotificationTesterTest.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/notification/NotificationTesterTest.java new file mode 100644 index 000000000..89d8d42c8 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/notification/NotificationTesterTest.java @@ -0,0 +1,182 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.notification; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.testbench.unit.UIUnitTest; +import com.vaadin.testbench.unit.ViewPackages; + +@ViewPackages +class NotificationTesterTest extends UIUnitTest { + + @BeforeEach + public void registerView() { + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(NotificationView.class); + navigate(NotificationView.class); + } + + @Test + void showAndAutoClose_attachedAndDetached() { + Notification notification = Notification.show("Some text"); + roundTrip(); + + Assertions.assertTrue(notification.isAttached(), + "Notification should be attached after show"); + + NotificationTester notification_ = test(NotificationTester.class, + notification); + notification_.autoClose(); + + Assertions.assertFalse(notification.isAttached(), + "Notification should not be attached after auto-close"); + } + + @Test + void programmaticallyClose_notificationIsDetached() { + Notification notification = Notification.show("Some text"); + roundTrip(); + + notification.close(); + + Assertions.assertFalse(notification.isAttached(), + "Notification should not be attached after close"); + } + + @Test + void notOpenedNotification_isNotUsable() { + Notification notification = new Notification("Not Opened"); + + Assertions.assertFalse(test(notification).isUsable(), + "Not opened Notification shouldn't be usable"); + } + + @Test + void openedNotification_isUsable() { + String notificationText = "Opened notification"; + Notification notification = Notification.show(notificationText); + roundTrip(); + + Assertions.assertTrue(test(notification).isUsable(), + "Opened Notification should be usable"); + } + + @Test + void disabledNotification_isUsable() { + String notificationText = "Opened disabled notification"; + Notification notification = Notification.show(notificationText); + notification.setEnabled(false); + roundTrip(); + + Assertions.assertTrue(test(notification).isUsable(), + "Disabled Notification should be usable"); + } + + @Test + void hiddenNotification_isNotUsable() { + String notificationText = "Opened hidden notification"; + Notification notification = Notification.show(notificationText); + notification.setVisible(false); + roundTrip(); + + Assertions.assertFalse(test(notification).isUsable(), + "Hidden Notification should not be usable"); + } + + @Test + void closedNotification_notFlushed_isNotUsable() { + String notificationText = "Opened notification"; + Notification notification = Notification.show(notificationText); + roundTrip(); + notification.close(); + + Assertions.assertFalse(test(notification).isUsable(), + "Closed Notification should not be usable"); + } + + @Test + void autoClose_displayedNotification_isNotUsable() { + Notification notification = Notification.show("Some text"); + roundTrip(); + + NotificationTester notification_ = test(NotificationTester.class, + notification); + notification_.autoClose(); + + Assertions.assertFalse(notification_.isUsable(), + "Notification should not be usable after auto-close"); + } + + @Test + void autoClose_notOpenedNotification_throws() { + Notification notification = new Notification("Some text"); + + Assertions.assertThrows(IllegalStateException.class, + test(notification)::autoClose, + "Auto-close not opened notification should fail"); + } + + @Test + void autoClose_closedNotification_throws() { + Notification notification = Notification.show("Some text"); + roundTrip(); + + NotificationTester notification_ = test(NotificationTester.class, + notification); + notification_.autoClose(); + Assertions.assertThrows(IllegalStateException.class, + notification_::autoClose, + "Auto-close already closed notification should fail"); + } + + @Test + void autoClose_notificationWithDisabledAutoClose_throws() { + Notification notification = Notification.show("Some text"); + notification.setDuration(0); + roundTrip(); + + Assertions.assertThrows(IllegalStateException.class, + test(notification)::autoClose, + "Auto-close notification with auto-close disabled should fail"); + } + + @Test + void getText_displayedNotification_textContentAvailable() { + String notificationText = "Opened notification"; + Notification notification = Notification.show(notificationText); + roundTrip(); + + Assertions.assertEquals(notificationText, test(notification).getText()); + } + + @Test + void getText_notOpenedNotification_throws() { + Notification notification = new Notification("Some text"); + + Assertions.assertThrows(IllegalStateException.class, + test(notification)::getText, + "Getting text from not opened notification should fail"); + } + + @Test + void getText_closedNotification_throws() { + Notification notification = Notification.show("Some text"); + notification.close(); + roundTrip(); + + Assertions.assertThrows(IllegalStateException.class, + test(notification)::getText, + "Getting text from closed notification should fail"); + } + +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/notification/NotificationView.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/notification/NotificationView.java new file mode 100644 index 000000000..097bb2e07 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/notification/NotificationView.java @@ -0,0 +1,19 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.notification; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.router.Route; + +@Tag("div") +@Route(value = "notification", registerAtStartup = false) +public class NotificationView extends Component { + +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/radiobutton/RadioButtonGroupTesterTest.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/radiobutton/RadioButtonGroupTesterTest.java new file mode 100644 index 000000000..bffc0fd38 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/radiobutton/RadioButtonGroupTesterTest.java @@ -0,0 +1,82 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.radiobutton; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.testbench.unit.UIUnitTest; + +class RadioButtonGroupTesterTest extends UIUnitTest { + + RadioButtonView view; + RadioButtonGroupTester, RadioButtonView.Name> buttonGroup_; + + @BeforeEach + public void registerView() { + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(RadioButtonView.class); + view = navigate(RadioButtonView.class); + buttonGroup_ = test(view.radioButtonGroup); + } + + @Test + void selectItem_selectCorrectItem() { + buttonGroup_.selectItem("test-bar"); + Assertions.assertEquals(view.items.get(1), buttonGroup_.getSelected()); + + buttonGroup_.selectItem("test-jay"); + Assertions.assertEquals(view.items.get(3), buttonGroup_.getSelected()); + } + + @Test + void deselectAll_noItemsSelected() { + view.radioButtonGroup.setValue(view.items.get(0)); + + buttonGroup_.deselectItem(); + RadioButtonView.Name selectedItem = buttonGroup_.getSelected(); + Assertions.assertNull(selectedItem, + "Expecting no selection, but got " + selectedItem); + } + + @Test + void selectItem_notExisting_throws() { + Assertions.assertThrows(IllegalArgumentException.class, + () -> buttonGroup_.selectItem("jay")); + } + + @Test + void selectItem_itemDisabled_throws() { + view.radioButtonGroup + .setItemEnabledProvider(n -> n.getName().startsWith("b")); + + // Items enabled, should work + buttonGroup_.selectItem("test-bar"); + buttonGroup_.selectItem("test-baz"); + + Assertions.assertThrows(IllegalStateException.class, + () -> buttonGroup_.selectItem("test-foo")); + Assertions.assertThrows(IllegalStateException.class, + () -> buttonGroup_.selectItem("test-jay")); + } + + @Test + void readOnly_isNotUsable() { + view.radioButtonGroup.setReadOnly(true); + + Assertions.assertThrows(IllegalStateException.class, + () -> buttonGroup_.selectItem("test-foo")); + Assertions.assertThrows(IllegalStateException.class, + () -> buttonGroup_.deselectItem()); + + } + +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/radiobutton/RadioButtonTesterTest.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/radiobutton/RadioButtonTesterTest.java new file mode 100644 index 000000000..986961649 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/radiobutton/RadioButtonTesterTest.java @@ -0,0 +1,81 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.radiobutton; + +import java.util.concurrent.atomic.AtomicBoolean; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.testbench.unit.UIUnitTest; +import com.vaadin.testbench.unit.ViewPackages; + +@ViewPackages +class RadioButtonTesterTest extends UIUnitTest { + + RadioButtonView view; + RadioButtonTester, String> radioButton_; + + @BeforeEach + public void registerView() { + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(RadioButtonView.class); + view = navigate(RadioButtonView.class); + radioButton_ = test(view.radioButton); + } + + @Test + void click_usable_valueChanges() { + Assertions.assertFalse(view.radioButton.isCheckedBoolean(), + "Expecting radioButton initial state not to be checked"); + + radioButton_.click(); + Assertions.assertTrue(view.radioButton.isCheckedBoolean(), + "Expecting radioButton to be checked, but was not"); + } + + @Test + void click_usable_checkedChangeFired() { + AtomicBoolean checkedChange = new AtomicBoolean(); + view.radioButton.getElement().addPropertyChangeListener("checked", + (ev -> checkedChange.set(true))); + Assertions.assertFalse(view.radioButton.isCheckedBoolean(), + "Expecting radioButton not to be checked, but was"); + + radioButton_.click(); + Assertions.assertTrue(checkedChange.get(), + "Expected radioButton change event to be fired, but was not"); + Assertions.assertTrue(view.radioButton.isCheckedBoolean(), + "Expecting radioButton not to be checked, but was"); + } + + @Test + void click_disabled_throws() { + view.radioButton.setEnabled(false); + Assertions.assertThrows(IllegalStateException.class, + radioButton_::click); + } + + @Test + void click_disabledByProperty_throws() { + view.radioButton.setDisabled(true); + Assertions.assertThrows(IllegalStateException.class, + radioButton_::click); + } + + @Test + void click_invisible_throws() { + view.radioButton.setVisible(false); + Assertions.assertThrows(IllegalStateException.class, + radioButton_::click); + } + +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/radiobutton/RadioButtonView.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/radiobutton/RadioButtonView.java new file mode 100644 index 000000000..2e8a957b5 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/radiobutton/RadioButtonView.java @@ -0,0 +1,55 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.radiobutton; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.HasComponents; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.router.Route; + +@Tag("div") +@Route(value = "checkbox", registerAtStartup = false) +public class RadioButtonView extends Component implements HasComponents { + + RadioButton radioButton = new RadioButton<>("test", "item"); + + List items = Stream.of("foo", "bar", "baz", "jay").map(Name::new) + .collect(Collectors.toList()); + + RadioButtonGroup radioButtonGroup = new RadioButtonGroup<>(); + + public RadioButtonView() { + add(radioButton); + + radioButtonGroup.setItems(items); + radioButtonGroup.setItemLabelGenerator(item -> "test-" + item); + add(radioButtonGroup); + } + + public static class Name { + String name; + + public Name(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + @Override + public String toString() { + return name; + } + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/routerlink/AbstractTargetView.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/routerlink/AbstractTargetView.java new file mode 100644 index 000000000..2fb29a8df --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/routerlink/AbstractTargetView.java @@ -0,0 +1,23 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.routerlink; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.HasComponents; +import com.vaadin.flow.component.html.Span; + +abstract class AbstractTargetView extends Component implements HasComponents { + + final Span message; + + public AbstractTargetView() { + message = new Span(); + add(message); + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/routerlink/RouterLinkQueryParameterTargetView.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/routerlink/RouterLinkQueryParameterTargetView.java new file mode 100644 index 000000000..17700dad7 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/routerlink/RouterLinkQueryParameterTargetView.java @@ -0,0 +1,39 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.routerlink; + +import java.util.stream.Collectors; + +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.router.BeforeEnterEvent; +import com.vaadin.flow.router.BeforeEnterObserver; +import com.vaadin.flow.router.Route; + +@Tag(Tag.DIV) +@Route(value = RouterLinkQueryParameterTargetView.ROUTE, registerAtStartup = false) +public class RouterLinkQueryParameterTargetView extends AbstractTargetView + implements BeforeEnterObserver { + + public static final String ROUTE = "router-link-query-parameter-target"; + + @Override + public void beforeEnter(BeforeEnterEvent event) { + var queryParameters = event.getLocation().getQueryParameters(); + message.setText( + "Query Parameter Target View: { " + + queryParameters.getParameters().entrySet().stream() + .map(entry -> entry.getKey() + " = [" + + entry.getValue().stream().sorted() + .collect(Collectors + .joining(", ")) + + "]") + .sorted().collect(Collectors.joining("; ")) + + " }"); + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/routerlink/RouterLinkRouteParameterTargetView.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/routerlink/RouterLinkRouteParameterTargetView.java new file mode 100644 index 000000000..6aed85013 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/routerlink/RouterLinkRouteParameterTargetView.java @@ -0,0 +1,39 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.routerlink; + +import java.util.stream.Collectors; + +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.router.BeforeEnterEvent; +import com.vaadin.flow.router.BeforeEnterObserver; +import com.vaadin.flow.router.Route; + +@Tag(Tag.DIV) +@Route(value = RouterLinkRouteParameterTargetView.ROUTE + + RouterLinkRouteParameterTargetView.ROUTE_PARAMETERS, registerAtStartup = false) +public class RouterLinkRouteParameterTargetView extends AbstractTargetView + implements BeforeEnterObserver { + + public static final String ROUTE = "router-link-route-parameter-target"; + public static final String ROUTE_PARAMETERS = "/:segment1?/static/:segment2/:segment3*"; + + @Override + public void beforeEnter(BeforeEnterEvent event) { + var routeParameters = event.getRouteParameters(); + + message.setText( + "Route Parameter Target View: { " + + routeParameters.getParameterNames().stream() + .map(name -> name + " = " + + routeParameters.get(name).orElse("")) + .sorted().collect(Collectors.joining("; ")) + + " }"); + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/routerlink/RouterLinkStaticTargetView.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/routerlink/RouterLinkStaticTargetView.java new file mode 100644 index 000000000..9fc862a0b --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/routerlink/RouterLinkStaticTargetView.java @@ -0,0 +1,25 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.routerlink; + +import com.vaadin.flow.component.HasComponents; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.router.Route; + +@Tag(Tag.DIV) +@Route(value = RouterLinkStaticTargetView.ROUTE, registerAtStartup = false) +public class RouterLinkStaticTargetView extends AbstractTargetView + implements HasComponents { + + public static final String ROUTE = "router-link-static-target"; + + public RouterLinkStaticTargetView() { + message.setText("Static Target View"); + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/routerlink/RouterLinkTesterTest.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/routerlink/RouterLinkTesterTest.java new file mode 100644 index 000000000..2f7dae380 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/routerlink/RouterLinkTesterTest.java @@ -0,0 +1,150 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.routerlink; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.flow.router.RouterLink; +import com.vaadin.testbench.unit.UIUnitTest; +import com.vaadin.testbench.unit.ViewPackages; + +@ViewPackages +class RouterLinkTesterTest extends UIUnitTest { + + private RouterLinkView routerLinkView; + + @BeforeEach + void init() { + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(RouterLinkView.class); + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(RouterLinkStaticTargetView.class); + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(RouterLinkUrlParameterTargetView.class); + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(RouterLinkQueryParameterTargetView.class); + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(RouterLinkRouteParameterTargetView.class); + + routerLinkView = navigate(RouterLinkView.class); + } + + @Test + void routerLink_targetless() { + // get router link + var $targetlessRouterLink = test(routerLinkView.targetlessRouterLink); + Assertions.assertNotNull($targetlessRouterLink, + "Tester for targetless RouterLink not initialized."); + + // verify its href + Assertions.assertEquals("", $targetlessRouterLink.getHref()); + + // verify its click action fails due to no navigation target + Assertions.assertThrows(IllegalStateException.class, + $targetlessRouterLink::click); + } + + @Test + void routerLink_static() { + // get router link + var $staticRouterLink = test(routerLinkView.staticRouterLink); + Assertions.assertNotNull($staticRouterLink, + "Tester for static RouterLink not initialized."); + + // verify its href + Assertions.assertEquals(RouterLinkStaticTargetView.ROUTE, + $staticRouterLink.getHref()); + + assertNavigationSucceeded($staticRouterLink, + RouterLinkStaticTargetView.class, "Static Target View"); + } + + @Test + void routerLink_emptyUrlParameter() { + var $emptyUrlParameterRouterLink = test( + routerLinkView.emptyUrlParameterRouterLink); + Assertions.assertNotNull($emptyUrlParameterRouterLink, + "Tester for empty URL parameter RouterLink not initialized."); + + // verify its href + Assertions.assertEquals(RouterLinkUrlParameterTargetView.ROUTE, + $emptyUrlParameterRouterLink.getHref()); + + assertNavigationSucceeded($emptyUrlParameterRouterLink, + RouterLinkUrlParameterTargetView.class, + "URL Parameter Target View: { }"); + } + + @Test + void routerLink_urlParameter() { + var $urlParameterRouterLink = test( + routerLinkView.urlParameterRouterLink); + Assertions.assertNotNull($urlParameterRouterLink, + "Tester for URL parameter RouterLink not initialized."); + + // verify its href + Assertions.assertEquals( + RouterLinkUrlParameterTargetView.ROUTE + "/parameter-value", + $urlParameterRouterLink.getHref()); + + assertNavigationSucceeded($urlParameterRouterLink, + RouterLinkUrlParameterTargetView.class, + "URL Parameter Target View: { parameter-value }"); + } + + @Test + void routerLink_queryParameter() { + var $queryParameterRouterLink = test( + routerLinkView.queryParameterRouterLink); + Assertions.assertNotNull($queryParameterRouterLink, + "Tester for QueryParameter RouterLink not initialized."); + + // verify its href + Assertions.assertEquals(RouterLinkQueryParameterTargetView.ROUTE + + "?parameter2=parameter2-value1¶meter2=parameter2-value2¶meter1=parameter1-value", + $queryParameterRouterLink.getHref()); + + assertNavigationSucceeded($queryParameterRouterLink, + RouterLinkQueryParameterTargetView.class, + "Query Parameter Target View: { parameter1 = [parameter1-value]; parameter2 = [parameter2-value1, parameter2-value2] }"); + } + + @Test + void routerLink_routeParameter() { + var $routeParameterRouterLink = test( + routerLinkView.routeParameterRouterLink); + Assertions.assertNotNull($routeParameterRouterLink, + "Tester for RouteParameter RouterLink not initialized."); + + // verify its href + Assertions.assertEquals(RouterLinkRouteParameterTargetView.ROUTE + + "/static/segment2-value/segment3-value1/segment3-value2", + $routeParameterRouterLink.getHref()); + + assertNavigationSucceeded($routeParameterRouterLink, + RouterLinkRouteParameterTargetView.class, + "Route Parameter Target View: { segment2 = segment2-value; segment3 = segment3-value1/segment3-value2 }"); + } + + private void assertNavigationSucceeded(RouterLinkTester tester, + Class expectedTarget, + String expectedMessage) { + // verify its navigate action returns correct target + var targetView = tester.navigate(); + Assertions.assertInstanceOf(expectedTarget, targetView); + Assertions.assertSame(targetView, getCurrentView()); + + // verify navigation target is correct + Assertions.assertEquals(expectedMessage, + expectedTarget.cast(targetView).message.getText()); + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/routerlink/RouterLinkUrlParameterTargetView.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/routerlink/RouterLinkUrlParameterTargetView.java new file mode 100644 index 000000000..6462c5322 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/routerlink/RouterLinkUrlParameterTargetView.java @@ -0,0 +1,31 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.routerlink; + +import com.vaadin.flow.component.HasComponents; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.router.BeforeEvent; +import com.vaadin.flow.router.HasUrlParameter; +import com.vaadin.flow.router.OptionalParameter; +import com.vaadin.flow.router.Route; + +@Tag(Tag.DIV) +@Route(value = RouterLinkUrlParameterTargetView.ROUTE, registerAtStartup = false) +public class RouterLinkUrlParameterTargetView extends AbstractTargetView + implements HasComponents, HasUrlParameter { + + public static final String ROUTE = "router-link-url-parameter-target"; + + @Override + public void setParameter(BeforeEvent event, + @OptionalParameter String parameter) { + message.setText("URL Parameter Target View: { " + + (parameter != null ? parameter : "") + " }"); + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/routerlink/RouterLinkView.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/routerlink/RouterLinkView.java new file mode 100644 index 000000000..1246a1d92 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/routerlink/RouterLinkView.java @@ -0,0 +1,71 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.routerlink; + +import java.util.Map; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.HasComponents; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.router.QueryParameters; +import com.vaadin.flow.router.Route; +import com.vaadin.flow.router.RouteParameters; +import com.vaadin.flow.router.RouterLink; + +@Tag(Tag.DIV) +@Route(value = RouterLinkView.ROUTE, registerAtStartup = false) +public class RouterLinkView extends Component implements HasComponents { + + public static final String ROUTE = "router-link-test"; + + final RouterLink targetlessRouterLink; + final RouterLink staticRouterLink; + final RouterLink emptyUrlParameterRouterLink; + final RouterLink urlParameterRouterLink; + final RouterLink queryParameterRouterLink; + final RouterLink routeParameterRouterLink; + + public RouterLinkView() { + // targetless router link + targetlessRouterLink = new RouterLink(); + targetlessRouterLink.setText("No Target"); + + // static router link + staticRouterLink = new RouterLink("Static Target", + RouterLinkStaticTargetView.class); + + // url parameter router link - empty + emptyUrlParameterRouterLink = new RouterLink( + "Empty URL Parameter Target", + RouterLinkUrlParameterTargetView.class); + + // url parameter router link - non-empty + urlParameterRouterLink = new RouterLink("URL Parameter Target", + RouterLinkUrlParameterTargetView.class, "parameter-value"); + + // query parameter router link + queryParameterRouterLink = new RouterLink("Query Parameter Target", + RouterLinkQueryParameterTargetView.class); + queryParameterRouterLink.setQueryParameters(QueryParameters.empty() + .merging("parameter1", "parameter1-value").merging("parameter2", + "parameter2-value1", "parameter2-value2")); + + // route parameter router link + routeParameterRouterLink = new RouterLink("Route Parameter Target", + RouterLinkRouteParameterTargetView.class, + new RouteParameters( + Map.ofEntries(Map.entry("segment2", "segment2-value"), + Map.entry("segment3", + "segment3-value1/segment3-value2")))); + + add(targetlessRouterLink, staticRouterLink, emptyUrlParameterRouterLink, + urlParameterRouterLink, queryParameterRouterLink, + routeParameterRouterLink); + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/select/SelectTesterTest.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/select/SelectTesterTest.java new file mode 100644 index 000000000..661bd2f61 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/select/SelectTesterTest.java @@ -0,0 +1,78 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.select; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.testbench.unit.UIUnitTest; +import com.vaadin.testbench.unit.ViewPackages; + +import static org.junit.jupiter.api.Assertions.assertIterableEquals; + +@ViewPackages +class SelectTesterTest extends UIUnitTest { + SelectView view; + + @BeforeEach + void init() { + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(SelectView.class); + view = navigate(SelectView.class); + } + + @Test + void getSuggestionItems_returnsAllItems() { + final SelectTester, String> select_ = test(view.select); + assertIterableEquals(view.items, select_.getSuggestionItems()); + + final SelectTester, SelectView.Person> person_ = test( + view.personSelect); + assertIterableEquals(view.people, person_.getSuggestionItems()); + } + + @Test + void stringSelect_getSuggestions_valuesEqualItems() { + final SelectTester, String> select_ = test(view.select); + assertIterableEquals(view.items, select_.getSuggestions()); + } + + @Test + void stringSelect_selectItem_selectsCorrectItem() { + final SelectTester, String> select_ = test(view.select); + Assertions.assertNull(select_.getSelected()); + + select_.selectItem("Fantasy"); + + Assertions.assertSame(view.items.get(2), select_.getSelected()); + + select_.selectItem(null); + + Assertions.assertNull(select_.getSelected(), + "Selecting null should clear selection"); + } + + @Test + void beanSelect_selectItem_selectsCorrectItem() { + final SelectTester, SelectView.Person> select_ = test( + view.personSelect); + Assertions.assertNull(select_.getSelected()); + + select_.selectItem("Space"); + + Assertions.assertSame(view.people.get(1), select_.getSelected()); + + select_.selectItem(null); + + Assertions.assertNull(select_.getSelected(), + "Selecting null should clear selection"); + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/select/SelectView.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/select/SelectView.java new file mode 100644 index 000000000..717a4daaf --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/select/SelectView.java @@ -0,0 +1,56 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.select; + +import java.util.Arrays; +import java.util.List; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.HasComponents; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.router.Route; + +@Tag("div") +@Route(value = "select", registerAtStartup = false) +public class SelectView extends Component implements HasComponents { + Select select; + Select personSelect; + List people = Arrays.asList(new Person("John", "Doe"), + new Person("Space", "Cat")); + List items = Arrays.asList("Good", "Omens", "Fantasy", "Drawing"); + + public SelectView() { + select = new Select<>(); + select.setItems(items); + + personSelect = new Select<>(); + personSelect.setItemLabelGenerator(Person::getFirst); + personSelect.setItems(people); + + add(select, personSelect); + } + + static class Person { + String first; + String last; + + public Person(String first, String last) { + this.first = first; + this.last = last; + } + + public String getFirst() { + return first; + } + + public String getLast() { + return last; + } + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/sidenav/SideNavTesterTest.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/sidenav/SideNavTesterTest.java new file mode 100644 index 000000000..50417fb74 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/sidenav/SideNavTesterTest.java @@ -0,0 +1,322 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.sidenav; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.vaadin.flow.component.HasElement; +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.testbench.unit.UIUnitTest; +import com.vaadin.testbench.unit.ViewPackages; + +@ViewPackages +class SideNavTesterTest extends UIUnitTest { + + SideNavView view; + SideNavTester sideNav_; + + @BeforeEach + void init() { + RouteConfiguration routeConfiguration = RouteConfiguration + .forApplicationScope(); + routeConfiguration.setAnnotatedRoute(SideNavView.class); + routeConfiguration.setAnnotatedRoute(TargetView.class); + navigateToSideNavView(); + } + + @Test + void sideNav_notVisible_throws() { + view.sideNav.setVisible(false); + Assertions.assertThrows(IllegalStateException.class, sideNav_::click); + Assertions.assertThrows(IllegalStateException.class, + () -> sideNav_.clickItem("Messages")); + } + + @Test + void sideNav_notAttached_throws() { + view.sideNav.removeFromParent(); + Assertions.assertThrows(IllegalStateException.class, sideNav_::click); + Assertions.assertThrows(IllegalStateException.class, + () -> sideNav_.clickItem("Messages")); + } + + @Test + void click_collapsible_sideNavExpandsAndCollapse() { + view.sideNav.setCollapsible(true); + Assertions.assertTrue(view.sideNav.isExpanded(), + "Expected SideNav to be expanded by default"); + sideNav_.click(); + Assertions.assertFalse(view.sideNav.isExpanded(), + "Expected SideNav to be collapsed after click on SideNav top"); + sideNav_.click(); + Assertions.assertTrue(view.sideNav.isExpanded(), + "Expected SideNav to be expanded again after second click on SideNav top"); + } + + @Test + void click_notCollapsible_noAction() { + view.sideNav.setCollapsible(false); + Assertions.assertTrue(view.sideNav.isExpanded(), + "Expected SideNav to be expanded by default"); + sideNav_.click(); + Assertions.assertTrue(view.sideNav.isExpanded(), + "Expected SideNav to be expanded because click should have no effect"); + sideNav_.click(); + Assertions.assertTrue(view.sideNav.isExpanded(), + "Expected SideNav to be expanded because click should have no effect"); + } + + @Test + void toggle_collapsible_sideNavExpandsAndCollapse() { + view.sideNav.setCollapsible(true); + Assertions.assertTrue(view.sideNav.isExpanded(), + "Expected SideNav to be expanded by default"); + sideNav_.toggle(); + Assertions.assertFalse(view.sideNav.isExpanded(), + "Expected SideNav to be collapsed after click on SideNav top"); + sideNav_.toggle(); + Assertions.assertTrue(view.sideNav.isExpanded(), + "Expected SideNav to be expanded again after second click on SideNav top"); + } + + @Test + void toggle_notCollapsible_throws() { + view.sideNav.setCollapsible(false); + Assertions.assertThrows(IllegalStateException.class, sideNav_::toggle); + } + + @Test + void clickItem_byText_navigationHappens() { + sideNav_.clickItem("Messages"); + assertNavigatedToTargetViewWithParam("N/A"); + } + + @Test + void clickItem_byText_leafWithoutPath_noNavigation() { + sideNav_.clickItem("No Link"); + assertNoNavigation(); + } + + @Test + void clickItem_byText_parentWithoutPath_expandsAndCollapse() { + Assertions.assertFalse(view.adminSection.isExpanded(), + "Expected SideNavItem to be collapsed by default"); + sideNav_.clickItem("Admin"); + Assertions.assertTrue(view.adminSection.isExpanded(), + "Expected SideNavItem to be expanded after click"); + sideNav_.clickItem("Admin"); + Assertions.assertFalse(view.adminSection.isExpanded(), + "Expected SideNav to be collapsed again after second click"); + } + + @Test + void clickItem_notExisting_throws() { + String label = "Not existing item"; + IllegalArgumentException exception = Assertions.assertThrowsExactly( + IllegalArgumentException.class, + () -> sideNav_.clickItem(label)); + Assertions.assertTrue(exception.getMessage() + .contains("Cannot find SideNav item '" + label + "'")); + } + + @Test + void clickItem_multipleMatches_throws() { + IllegalStateException exception = Assertions.assertThrows( + IllegalStateException.class, + () -> sideNav_.clickItem("Top Level Duplicated")); + Assertions.assertTrue(exception.getMessage() + .contains("Found 2 items with label 'Top Level Duplicated'")); + } + + @Test + void clickItem_hidden_throws() { + view.adminSection.setVisible(false); + IllegalStateException exception = Assertions.assertThrows( + IllegalStateException.class, () -> sideNav_.clickItem("Admin")); + Assertions.assertTrue(exception.getMessage().contains("SideNavItem")); + Assertions.assertTrue(exception.getMessage().contains("is not usable")); + } + + @Test + void clickItem_nested_navigationHappens() { + view.adminSection.setExpanded(true); + view.securitySection.setExpanded(true); + sideNav_.clickItem("Admin", "Security", "Users"); + assertNavigatedToTargetViewWithParam("users"); + } + + @Test + void clickItem_nestedItemHidden_throws() { + view.adminSection.setExpanded(true); + view.securitySection.setVisible(false); + IllegalStateException exception = Assertions.assertThrowsExactly( + IllegalStateException.class, + () -> sideNav_.clickItem("Admin", "Security", "Users")); + Assertions.assertTrue(exception.getMessage().contains("SideNavItem")); + Assertions.assertTrue(exception.getMessage().contains("Security")); + Assertions.assertTrue(exception.getMessage().contains("is not usable")); + } + + @Test + void clickItem_nestedItem_parentCollapsed_throws() { + view.adminSection.setExpanded(false); + view.securitySection.setExpanded(false); + IllegalStateException exception = Assertions.assertThrowsExactly( + IllegalStateException.class, + () -> sideNav_.clickItem("Admin", "Security", "Users")); + Assertions.assertTrue(exception.getMessage() + .contains("Cannot find SideNav item with label 'Security'")); + Assertions.assertTrue(exception.getMessage() + .contains("on path 'Admin / Security / Users'")); + Assertions.assertTrue(exception.getMessage() + .contains("parent item 'Admin' is collapsed")); + } + + @Test + void clickItem_nestedWrongPath_throws() { + view.adminSection.setExpanded(true); + IllegalArgumentException exception = Assertions.assertThrowsExactly( + IllegalArgumentException.class, + () -> sideNav_.clickItem("Admin", "Configuration", "Users")); + Assertions.assertTrue(exception.getMessage().contains( + "Cannot find SideNav item 'Configuration' on path Admin / Configuration / Users")); + } + + @Test + void clickItem_multipleNestedMatches_throws() { + IllegalStateException exception = Assertions.assertThrows( + IllegalStateException.class, + () -> sideNav_.clickItem("Nested duplicated", "Duplicated")); + Assertions.assertTrue(exception.getMessage() + .contains("Found 2 items with label 'Duplicated'")); + Assertions.assertTrue(exception.getMessage() + .contains("on path Nested duplicated / Duplicated")); + } + + @Test + void expandAndClickItem_nested_expandCollapsedItems() { + sideNav_.expandAndClickItem("Admin", "Security", "Users"); + assertNavigatedToTargetViewWithParam("users"); + + Assertions.assertTrue(view.adminSection.isExpanded(), + "Expected 'Admin' item to be expanded"); + Assertions.assertTrue(view.securitySection.isExpanded(), + "Expected 'Admin / Security' item to be expanded"); + } + + @Test + void toggleItem_expandsAndCollapse() { + Assertions.assertFalse(view.adminSection.isExpanded(), + "Expected Admin SideNavItem to be collapsed by default"); + sideNav_.toggleItem("Admin"); + Assertions.assertTrue(view.adminSection.isExpanded(), + "Expected Admin SideNavItem to be expanded after click"); + sideNav_.toggleItem("Admin"); + Assertions.assertFalse(view.adminSection.isExpanded(), + "Expected Admin SideNavItem to be collapsed again after second click"); + } + + @Test + void toggleItem_nestedItem_expandsAndCollapse() { + view.adminSection.setExpanded(true); + Assertions.assertFalse(view.securitySection.isExpanded(), + "Expected Security SideNavItem to be collapsed by default"); + sideNav_.toggleItem("Admin", "Security"); + Assertions.assertTrue(view.securitySection.isExpanded(), + "Expected Security SideNavItem to be expanded after click"); + sideNav_.toggleItem("Admin", "Security"); + Assertions.assertFalse(view.securitySection.isExpanded(), + "Expected Security SideNavItem to be collapsed again after second click"); + } + + @Test + void toggleItem_nestedItemHidden_throws() { + view.adminSection.setExpanded(true); + view.securitySection.setVisible(false); + IllegalStateException exception = Assertions.assertThrowsExactly( + IllegalStateException.class, + () -> sideNav_.toggleItem("Admin", "Security")); + Assertions.assertTrue(exception.getMessage().contains("SideNavItem")); + Assertions.assertTrue(exception.getMessage().contains("Security")); + Assertions.assertTrue(exception.getMessage().contains("is not usable")); + } + + @Test + void toggleItem_nestedItem_parentCollapsed_throws() { + view.adminSection.setExpanded(false); + view.securitySection.setExpanded(false); + IllegalStateException exception = Assertions.assertThrowsExactly( + IllegalStateException.class, + () -> sideNav_.toggleItem("Admin", "Security")); + Assertions.assertTrue(exception.getMessage() + .contains("Cannot find SideNav item with label 'Security'")); + Assertions.assertTrue( + exception.getMessage().contains("on path 'Admin / Security")); + Assertions.assertTrue(exception.getMessage() + .contains("parent item 'Admin' is collapsed")); + } + + @Test + void toggleItem_nestedWrongPath_throws() { + view.adminSection.setExpanded(true); + IllegalArgumentException exception = Assertions.assertThrowsExactly( + IllegalArgumentException.class, + () -> sideNav_.toggleItem("Admin", "Configuration", "Users")); + Assertions.assertTrue(exception.getMessage().contains( + "Cannot find SideNav item 'Configuration' on path Admin / Configuration / Users")); + } + + @Test + void toggleItem_leafItem_throws() { + IllegalStateException exception = Assertions.assertThrows( + IllegalStateException.class, + () -> sideNav_.toggleItem("No Link")); + Assertions.assertTrue(exception.getMessage() + .contains("Toggle button cannot be clicked")); + + view.adminSection.setExpanded(true); + view.securitySection.setExpanded(true); + exception = Assertions.assertThrows(IllegalStateException.class, + () -> sideNav_.toggleItem("Admin", "Security", "Users")); + Assertions.assertTrue(exception.getMessage() + .contains("Toggle button cannot be clicked")); + } + + @Test + void toggleItem_multipleMatches_throws() { + IllegalStateException exception = Assertions.assertThrows( + IllegalStateException.class, + () -> sideNav_.toggleItem("Nested duplicated", "Duplicated")); + Assertions.assertTrue(exception.getMessage().contains( + "Found 2 items with label 'Duplicated' on path Nested duplicated / Duplicated")); + } + + private void assertNavigatedToTargetViewWithParam( + String expectedParamValue) { + HasElement currentView = getCurrentView(); + Assertions.assertInstanceOf(TargetView.class, currentView, + "Expecting click on SideNav item to navigate to TargetView, but current view is " + + currentView.getClass()); + Assertions.assertEquals(((TargetView) currentView).parameter, + expectedParamValue); + } + + private void assertNoNavigation() { + HasElement currentView = getCurrentView(); + Assertions.assertSame(view, currentView, + "Expecting click on SideNav item without path to stay on current view"); + } + + private void navigateToSideNavView() { + view = navigate(SideNavView.class); + sideNav_ = test(view.sideNav); + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/sidenav/SideNavView.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/sidenav/SideNavView.java new file mode 100644 index 000000000..a294b47d9 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/sidenav/SideNavView.java @@ -0,0 +1,66 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.sidenav; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.HasComponents; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.component.icon.VaadinIcon; +import com.vaadin.flow.router.Route; +import com.vaadin.flow.router.RouteParameters; + +@Tag("div") +@Route(value = "sidenav", registerAtStartup = false) +public class SideNavView extends Component implements HasComponents { + + final SideNav sideNav; + final SideNavItem adminSection; + final SideNavItem securitySection; + + public SideNavView() { + sideNav = new SideNav("Menu"); + SideNavItem messagesLink = new SideNavItem("Messages", TargetView.class, + VaadinIcon.ENVELOPE.create()); + messagesLink.addItem(new SideNavItem("Inbox", TargetView.class, + new RouteParameters("param", "inbox"), + VaadinIcon.INBOX.create())); + messagesLink.addItem(new SideNavItem("Sent", TargetView.class, + new RouteParameters("param", "sent"), + VaadinIcon.PAPERPLANE.create())); + messagesLink.addItem(new SideNavItem("Trash", "sidenav-target/trash", + VaadinIcon.TRASH.create())); + + adminSection = new SideNavItem("Admin"); + adminSection.setPrefixComponent(VaadinIcon.COG.create()); + securitySection = new SideNavItem("Security"); + adminSection.addItem(securitySection); + + securitySection.addItem(new SideNavItem("Users", TargetView.class, + new RouteParameters("param", "users"), + VaadinIcon.GROUP.create())); + securitySection.addItem(new SideNavItem("Permissions", TargetView.class, + new RouteParameters("param", "permissions"), + VaadinIcon.KEY.create())); + securitySection.addItem(new SideNavItem("No Op")); + + sideNav.addItem(messagesLink, adminSection); + sideNav.addItem(new SideNavItem("No Link")); + + // Duplicated items + sideNav.addItem(new SideNavItem("Top Level Duplicated")); + sideNav.addItem(new SideNavItem("Top Level Duplicated")); + SideNavItem nestedDuplicates = new SideNavItem("Nested duplicated"); + nestedDuplicates.setExpanded(true); + nestedDuplicates.addItem(new SideNavItem("Duplicated"), + new SideNavItem("Duplicated")); + sideNav.addItem(nestedDuplicates); + + add(sideNav); + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/sidenav/TargetView.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/sidenav/TargetView.java new file mode 100644 index 000000000..bc4bcaec6 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/sidenav/TargetView.java @@ -0,0 +1,29 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.sidenav; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.router.BeforeEnterEvent; +import com.vaadin.flow.router.BeforeEnterObserver; +import com.vaadin.flow.router.Route; +import com.vaadin.flow.router.RouteAlias; + +@Tag("div") +@Route(value = "sidenav-target", registerAtStartup = false) +@RouteAlias("sidenav-target/:param") +public class TargetView extends Component implements BeforeEnterObserver { + + String parameter; + + @Override + public void beforeEnter(BeforeEnterEvent event) { + parameter = event.getRouteParameters().get("param").orElse("N/A"); + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/tabs/TabSheetTesterTest.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/tabs/TabSheetTesterTest.java new file mode 100644 index 000000000..b4f6ab7b8 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/tabs/TabSheetTesterTest.java @@ -0,0 +1,258 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.tabs; + +import java.util.concurrent.atomic.AtomicReference; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.testbench.unit.UIUnitTest; +import com.vaadin.testbench.unit.ViewPackages; + +@ViewPackages +class TabSheetTesterTest extends UIUnitTest { + + TabSheetView view; + TabSheetTester tabs_; + + @BeforeEach + void init() { + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(TabSheetView.class); + view = navigate(TabSheetView.class); + tabs_ = test(view.tabs); + } + + @Test + void select_notUsable_throws() { + view.tabs.setVisible(false); + Assertions.assertThrows(IllegalStateException.class, + () -> tabs_.select("Details")); + Assertions.assertThrows(IllegalStateException.class, + () -> tabs_.select(0)); + } + + @Test + void select_disabledTab_throws() { + view.payment.setEnabled(false); + Assertions.assertThrows(IllegalStateException.class, + () -> tabs_.select("Payment")); + Assertions.assertThrows(IllegalStateException.class, + () -> tabs_.select(1)); + } + + @Test + void select_hiddenTab_throws() { + view.payment.setVisible(false); + Assertions.assertThrows(IllegalStateException.class, + () -> tabs_.select("Payment")); + } + + @Test + void select_notExistingTab_throws() { + view.payment.setEnabled(false); + Assertions.assertThrows(IllegalArgumentException.class, + () -> tabs_.select("Summary")); + + Assertions.assertThrows(IllegalArgumentException.class, + () -> tabs_.select(4)); + } + + @Test + void select_byLabel_selectsTab() { + AtomicReference selectedTab = new AtomicReference<>(); + view.tabs.addSelectedChangeListener( + ev -> selectedTab.set(ev.getSelectedTab())); + + tabs_.select("Payment"); + Assertions.assertEquals(view.payment, selectedTab.get()); + + tabs_.select("Details"); + Assertions.assertEquals(view.details, selectedTab.get()); + + tabs_.select("Shipping"); + Assertions.assertEquals(view.shipping, selectedTab.get()); + } + + @Test + void select_byIndex_selectsTab() { + AtomicReference selectedTab = new AtomicReference<>(); + view.tabs.addSelectedChangeListener( + ev -> selectedTab.set(ev.getSelectedTab())); + + tabs_.select(1); + Assertions.assertEquals(view.payment, selectedTab.get()); + + tabs_.select(0); + Assertions.assertEquals(view.details, selectedTab.get()); + + tabs_.select(2); + Assertions.assertEquals(view.shipping, selectedTab.get()); + + tabs_.select(-1); + Assertions.assertNull(selectedTab.get()); + } + + @Test + void select_byIndex_indexRefersToVisibleTabs() { + view.details.setVisible(false); + AtomicReference selectedTab = new AtomicReference<>(); + view.tabs.addSelectedChangeListener( + ev -> selectedTab.set(ev.getSelectedTab())); + + // Payment now is the first visible tab + tabs_.select(0); + Assertions.assertEquals(view.payment, selectedTab.get()); + + view.payment.setVisible(false); + // Shipping is now is the only visible tab + tabs_.select(0); + Assertions.assertEquals(view.shipping, selectedTab.get()); + } + + @Test + void getTab_getsCorrectTab() { + Assertions.assertSame(view.details, tabs_.getTab("Details")); + Assertions.assertSame(view.payment, tabs_.getTab("Payment")); + Assertions.assertSame(view.shipping, tabs_.getTab("Shipping")); + + Assertions.assertSame(view.details, tabs_.getTab(0)); + Assertions.assertSame(view.payment, tabs_.getTab(1)); + Assertions.assertSame(view.shipping, tabs_.getTab(2)); + + view.details.setVisible(false); + // Payment now is the first visible tab + Assertions.assertSame(view.payment, tabs_.getTab(0)); + + view.payment.setVisible(false); + // Shipping is now is the only visible tab + Assertions.assertSame(view.shipping, tabs_.getTab(0)); + + // Now Details and Shipping + view.details.setVisible(true); + Assertions.assertSame(view.details, tabs_.getTab(0)); + Assertions.assertSame(view.shipping, tabs_.getTab(1)); + } + + @Test + void getTab_notExistingTab_throws() { + Assertions.assertThrows(IllegalArgumentException.class, + () -> tabs_.getTab("Summary")); + + Assertions.assertThrows(IllegalArgumentException.class, + () -> tabs_.getTab(4)); + Assertions.assertThrows(IllegalArgumentException.class, + () -> tabs_.getTab(-1)); + } + + @Test + void getTab_hidden_throws() { + view.payment.setVisible(false); + Assertions.assertThrows(IllegalStateException.class, + () -> tabs_.getTab("Payment")); + } + + @Test + void isSelected_getsTabState() { + view.tabs.setSelectedIndex(1); + + Assertions.assertFalse(tabs_.isSelected("Details"), + "Details tab is not selected, but got true"); + Assertions.assertTrue(tabs_.isSelected("Payment"), + "Payment tab is selected, but got false"); + Assertions.assertFalse(tabs_.isSelected("Shipping"), + "Shipping tab is not selected, but got true"); + + Assertions.assertFalse(tabs_.isSelected(0), + "Details tab at index 0 is not selected, but got true"); + Assertions.assertTrue(tabs_.isSelected(1), + "Payment tab at index 1 is selected, but got false"); + Assertions.assertFalse(tabs_.isSelected(2), + "Shipping tab at index 2 is not selected, but got true"); + } + + @Test + void isSelected_byIndex_invisibleTabsIgnored() { + view.payment.setVisible(false); + view.tabs.setSelectedTab(view.shipping); + + Assertions.assertFalse(tabs_.isSelected(0), + "Details tab at index 0 should be not selected, but got true"); + Assertions.assertTrue(tabs_.isSelected(1), + "Shipping tab at index 1 (payment hidden) should be selected, but got false"); + + view.payment.setVisible(true); + view.details.setVisible(false); + view.tabs.setSelectedTab(view.payment); + Assertions.assertTrue(tabs_.isSelected(0), + "Payment tab at index 0 (details hidden) should be selected, but got false"); + Assertions.assertFalse(tabs_.isSelected(1), + "Shipping tab at index 1 (details hidden) should be not selected, but got true"); + } + + @Test + void isSelected_notExistingTab_throws() { + Assertions.assertThrows(IllegalArgumentException.class, + () -> tabs_.isSelected("Summary")); + + Assertions.assertThrows(IllegalArgumentException.class, + () -> tabs_.isSelected(4)); + Assertions.assertThrows(IllegalArgumentException.class, + () -> tabs_.isSelected(-1)); + } + + @Test + void getTabContent_getsCorrectTab() { + Assertions.assertSame(view.detailsContent, + tabs_.getTabContent("Details")); + Assertions.assertSame(view.paymentContent, + tabs_.getTabContent("Payment")); + Assertions.assertSame(view.shippingContent, + tabs_.getTabContent("Shipping")); + + Assertions.assertSame(view.detailsContent, tabs_.getTabContent(0)); + Assertions.assertSame(view.paymentContent, tabs_.getTabContent(1)); + Assertions.assertSame(view.shippingContent, tabs_.getTabContent(2)); + + view.details.setVisible(false); + // Payment now is the first visible tab + Assertions.assertSame(view.paymentContent, tabs_.getTabContent(0)); + + view.payment.setVisible(false); + // Shipping is now is the only visible tab + Assertions.assertSame(view.shippingContent, tabs_.getTabContent(0)); + + // Now Details and Shipping + view.details.setVisible(true); + Assertions.assertSame(view.detailsContent, tabs_.getTabContent(0)); + Assertions.assertSame(view.shippingContent, tabs_.getTabContent(1)); + } + + @Test + void getTabContent_notExistingTab_throws() { + Assertions.assertThrows(IllegalArgumentException.class, + () -> tabs_.getTabContent("Summary")); + + Assertions.assertThrows(IllegalArgumentException.class, + () -> tabs_.getTabContent(4)); + Assertions.assertThrows(IllegalArgumentException.class, + () -> tabs_.getTabContent(-1)); + } + + @Test + void getTabContent_hidden_throws() { + view.payment.setVisible(false); + Assertions.assertThrows(IllegalStateException.class, + () -> tabs_.getTabContent("Payment")); + } + +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/tabs/TabSheetView.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/tabs/TabSheetView.java new file mode 100644 index 000000000..3a19690a1 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/tabs/TabSheetView.java @@ -0,0 +1,37 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.tabs; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.HasComponents; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.component.html.Span; +import com.vaadin.flow.router.Route; + +@Tag("div") +@Route(value = "tabsheet", registerAtStartup = false) +public class TabSheetView extends Component implements HasComponents { + + TabSheet tabs; + Tab details; + Span detailsContent = new Span("Details contents"); + Tab payment; + Span paymentContent = new Span("Payment contents"); + + Tab shipping; + Span shippingContent = new Span("Shipping contents"); + + public TabSheetView() { + tabs = new TabSheet(); + details = tabs.add(new Tab("Details"), detailsContent); + payment = tabs.add("Payment", paymentContent); + shipping = tabs.add("Shipping", shippingContent); + add(tabs); + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/tabs/TabsTesterTest.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/tabs/TabsTesterTest.java new file mode 100644 index 000000000..b9f3b6321 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/tabs/TabsTesterTest.java @@ -0,0 +1,209 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.tabs; + +import java.util.concurrent.atomic.AtomicReference; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.testbench.unit.UIUnitTest; +import com.vaadin.testbench.unit.ViewPackages; + +@ViewPackages +class TabsTesterTest extends UIUnitTest { + + TabsView view; + TabsTester tabs_; + + @BeforeEach + void init() { + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(TabsView.class); + view = navigate(TabsView.class); + tabs_ = test(view.tabs); + } + + @Test + void select_notUsable_throws() { + view.tabs.setEnabled(false); + Assertions.assertThrows(IllegalStateException.class, + () -> tabs_.select("Details")); + Assertions.assertThrows(IllegalStateException.class, + () -> tabs_.select(0)); + } + + @Test + void select_disableTabs_throws() { + view.payment.setEnabled(false); + Assertions.assertThrows(IllegalStateException.class, + () -> tabs_.select("Payment")); + Assertions.assertThrows(IllegalStateException.class, + () -> tabs_.select(1)); + } + + @Test + void select_notExistingTab_throws() { + view.payment.setEnabled(false); + Assertions.assertThrows(IllegalArgumentException.class, + () -> tabs_.select("Summary")); + + Assertions.assertThrows(IllegalArgumentException.class, + () -> tabs_.select(4)); + + } + + @Test + void select_byLabel_selectsTab() { + AtomicReference selectedTab = new AtomicReference<>(); + view.tabs.addSelectedChangeListener( + ev -> selectedTab.set(ev.getSelectedTab())); + + tabs_.select("Payment"); + Assertions.assertEquals(view.payment, selectedTab.get()); + + tabs_.select("Details"); + Assertions.assertEquals(view.details, selectedTab.get()); + + tabs_.select("Shipping"); + Assertions.assertEquals(view.shipping, selectedTab.get()); + } + + @Test + void select_byIndex_selectsTab() { + AtomicReference selectedTab = new AtomicReference<>(); + view.tabs.addSelectedChangeListener( + ev -> selectedTab.set(ev.getSelectedTab())); + + tabs_.select(1); + Assertions.assertEquals(view.payment, selectedTab.get()); + + tabs_.select(0); + Assertions.assertEquals(view.details, selectedTab.get()); + + tabs_.select(2); + Assertions.assertEquals(view.shipping, selectedTab.get()); + + tabs_.select(-1); + Assertions.assertNull(selectedTab.get()); + + } + + @Test + void select_byIndex_indexRefersToVisibleTabs() { + view.details.setVisible(false); + AtomicReference selectedTab = new AtomicReference<>(); + view.tabs.addSelectedChangeListener( + ev -> selectedTab.set(ev.getSelectedTab())); + + // Payment now is the first visible tab + tabs_.select(0); + Assertions.assertEquals(view.payment, selectedTab.get()); + + view.payment.setVisible(false); + // Shipping is now is the only visible tab + tabs_.select(0); + Assertions.assertEquals(view.shipping, selectedTab.get()); + } + + @Test + void getTab_getsCorrectTab() { + Assertions.assertSame(view.details, tabs_.getTab("Details")); + Assertions.assertSame(view.payment, tabs_.getTab("Payment")); + Assertions.assertSame(view.shipping, tabs_.getTab("Shipping")); + + Assertions.assertSame(view.details, tabs_.getTab(0)); + Assertions.assertSame(view.payment, tabs_.getTab(1)); + Assertions.assertSame(view.shipping, tabs_.getTab(2)); + + view.details.setVisible(false); + // Payment now is the first visible tab + Assertions.assertSame(view.payment, tabs_.getTab(0)); + + view.payment.setVisible(false); + // Shipping is now is the only visible tab + Assertions.assertSame(view.shipping, tabs_.getTab(0)); + + // Now Details and Shipping + view.details.setVisible(true); + Assertions.assertSame(view.details, tabs_.getTab(0)); + Assertions.assertSame(view.shipping, tabs_.getTab(1)); + + } + + @Test + void getTab_notExistingTab_throws() { + Assertions.assertThrows(IllegalArgumentException.class, + () -> tabs_.getTab("Summary")); + + Assertions.assertThrows(IllegalArgumentException.class, + () -> tabs_.getTab(4)); + Assertions.assertThrows(IllegalArgumentException.class, + () -> tabs_.getTab(-1)); + } + + @Test + void getTab_hidden_throws() { + view.payment.setVisible(false); + Assertions.assertThrows(IllegalStateException.class, + () -> tabs_.getTab("Payment")); + } + + @Test + void isSelected_getsTabState() { + view.tabs.setSelectedIndex(1); + + Assertions.assertFalse(tabs_.isSelected("Details"), + "Details tab is not selected, but got true"); + Assertions.assertTrue(tabs_.isSelected("Payment"), + "Payment tab is selected, but got false"); + Assertions.assertFalse(tabs_.isSelected("Shipping"), + "Shipping tab is not selected, but got true"); + + Assertions.assertFalse(tabs_.isSelected(0), + "Details tab at index 0 is not selected, but got true"); + Assertions.assertTrue(tabs_.isSelected(1), + "Payment tab at index 1 is selected, but got false"); + Assertions.assertFalse(tabs_.isSelected(2), + "Shipping tab at index 2 is not selected, but got true"); + } + + @Test + void isSelected_byIndex_invisibleTabsIgnored() { + view.payment.setVisible(false); + view.tabs.setSelectedTab(view.shipping); + + Assertions.assertFalse(tabs_.isSelected(0), + "Details tab at index 0 should be not selected, but got true"); + Assertions.assertTrue(tabs_.isSelected(1), + "Shipping tab at index 1 (payment hidden) should be selected, but got false"); + + view.payment.setVisible(true); + view.details.setVisible(false); + view.tabs.setSelectedTab(view.payment); + Assertions.assertTrue(tabs_.isSelected(0), + "Payment tab at index 0 (details hidden) should be selected, but got false"); + Assertions.assertFalse(tabs_.isSelected(1), + "Shipping tab at index 1 (details hidden) should be not selected, but got true"); + } + + @Test + void isSelected_notExistingTab_throws() { + Assertions.assertThrows(IllegalArgumentException.class, + () -> tabs_.isSelected("Summary")); + + Assertions.assertThrows(IllegalArgumentException.class, + () -> tabs_.isSelected(4)); + Assertions.assertThrows(IllegalArgumentException.class, + () -> tabs_.isSelected(-1)); + } + +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/tabs/TabsView.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/tabs/TabsView.java new file mode 100644 index 000000000..2c47e593c --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/tabs/TabsView.java @@ -0,0 +1,33 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.tabs; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.HasComponents; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.router.Route; + +@Tag("div") +@Route(value = "tabs", registerAtStartup = false) +public class TabsView extends Component implements HasComponents { + + Tabs tabs; + Tab details; + Tab payment; + Tab shipping; + + public TabsView() { + details = new Tab("Details"); + payment = new Tab("Payment"); + shipping = new Tab("Shipping"); + + tabs = new Tabs(details, payment, shipping); + add(tabs); + } +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/textfield/NumberFieldTesterTest.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/textfield/NumberFieldTesterTest.java new file mode 100644 index 000000000..e981970e0 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/textfield/NumberFieldTesterTest.java @@ -0,0 +1,136 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.textfield; + +import java.util.concurrent.atomic.AtomicReference; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.vaadin.flow.component.AbstractField; +import com.vaadin.flow.component.HasValue; +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.testbench.unit.UIUnitTest; +import com.vaadin.testbench.unit.ViewPackages; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +@ViewPackages +class NumberFieldTesterTest extends UIUnitTest { + + private NumberFieldView view; + + @BeforeEach + public void registerView() { + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(NumberFieldView.class); + view = navigate(NumberFieldView.class); + } + + @Test + public void readOnlyNumberField_isNotUsable() { + view.numberField.setReadOnly(true); + + final NumberFieldTester nf_ = test( + view.numberField); + + Assertions.assertFalse(nf_.isUsable(), + "Read only NumberField shouldn't be usable"); + } + + @Test + public void readOnlyNumberField_automatictester_readOnlyIsCheckedInUsable() { + view.numberField.setReadOnly(true); + + Assertions.assertFalse(test(view.numberField).isUsable(), + "Read only NumberField shouldn't be usable"); + } + + @Test + public void setNumberFieldValue_eventIsFired_valueIsSet() { + + AtomicReference value = new AtomicReference<>(null); + + view.numberField.addValueChangeListener( + (HasValue.ValueChangeListener>) event -> { + if (event.isFromClient()) { + value.compareAndSet(null, event.getValue()); + } + }); + + final NumberFieldTester nf_ = test( + view.numberField); + final Double newValue = 15d; + nf_.setValue(newValue); + + Assertions.assertEquals(newValue, value.get()); + } + + @Test + public void setIntegerFieldValue_eventIsFired_valueIsSet() { + + AtomicReference value = new AtomicReference<>(null); + + view.integerField.addValueChangeListener( + (HasValue.ValueChangeListener>) event -> { + value.compareAndSet(null, event.getValue()); + }); + + final NumberFieldTester inf_ = test( + view.integerField); + final Integer newValue = 15; + inf_.setValue(newValue); + + Assertions.assertEquals(newValue, value.get()); + } + + @Test + public void nonInteractableField_throwsOnSetValue() { + + view.numberField.getElement().setEnabled(false); + view.integerField.getElement().setEnabled(false); + final NumberFieldTester nf_ = test( + view.numberField); + final NumberFieldTester inf_ = test( + view.integerField); + + Assertions.assertThrows(IllegalStateException.class, + () -> nf_.setValue(12d), + "Setting value to a non interactable number field should fail"); + Assertions.assertThrows(IllegalStateException.class, + () -> inf_.setValue(12), + "Setting value to a non interactable integer field should fail"); + } + + @Test + public void maxValue_throwsExceptionForTooSmallValue() { + view.numberField.setMax(10.0); + + final NumberFieldTester nf_ = test( + view.numberField); + final Double newValue = 15d; + + assertThrows(IllegalArgumentException.class, + () -> nf_.setValue(newValue)); + } + + @Test + public void minValue_throwsExceptionForTooSmallValue() { + view.numberField.setMin(20.0); + + final NumberFieldTester nf_ = test( + view.numberField); + final Double newValue = 15d; + + assertThrows(IllegalArgumentException.class, + () -> nf_.setValue(newValue)); + } + +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/textfield/NumberFieldView.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/textfield/NumberFieldView.java new file mode 100644 index 000000000..e6b366023 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/textfield/NumberFieldView.java @@ -0,0 +1,29 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.textfield; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.HasComponents; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.router.Route; + +@Tag("div") +@Route(value = "number-fields", registerAtStartup = false) +public class NumberFieldView extends Component implements HasComponents { + NumberField numberField; + IntegerField integerField; + + public NumberFieldView() { + numberField = new NumberField(); + integerField = new IntegerField(); + + add(numberField, integerField); + } + +} diff --git a/browserless-test/junit6/src/test/java/com/vaadin/flow/component/textfield/TextAreaTesterTest.java b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/textfield/TextAreaTesterTest.java new file mode 100644 index 000000000..f8a49ced6 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/vaadin/flow/component/textfield/TextAreaTesterTest.java @@ -0,0 +1,140 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.textfield; + +import java.util.concurrent.atomic.AtomicReference; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.vaadin.flow.component.AbstractField; +import com.vaadin.flow.component.HasValue; +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.testbench.unit.UIUnitTest; +import com.vaadin.testbench.unit.ViewPackages; + +@ViewPackages +class TextAreaTesterTest extends UIUnitTest { + + private TextAreaView view; + + @BeforeEach + public void registerView() { + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(TextAreaView.class); + view = navigate(TextAreaView.class); + } + + @Test + public void readOnlyTextArea_isNotUsable() { + view.textArea.setReadOnly(true); + + final TextAreaTester