diff --git a/pom.xml b/pom.xml index 2f1f7f488..e28507a69 100644 --- a/pom.xml +++ b/pom.xml @@ -54,6 +54,8 @@ vaadin-testbench-unit vaadin-testbench-unit-junit5 vaadin-testbench-unit-quarkus + testing-support/uiunit-test-components + testing-support/uiunit-component-testers diff --git a/testing-support/uiunit-component-testers/pom.xml b/testing-support/uiunit-component-testers/pom.xml new file mode 100644 index 000000000..201e59079 --- /dev/null +++ b/testing-support/uiunit-component-testers/pom.xml @@ -0,0 +1,63 @@ + + + + + 4.0.0 + + com.vaadin + vaadin-testbench-parent + 10.0-SNAPSHOT + ../../pom.xml + + + uiunit-component-testers + jar + + + true + + + + + com.vaadin + flow-server + ${flow.version} + provided + + + com.vaadin + vaadin-testbench-unit-shared + ${project.version} + provided + + + com.vaadin + uiunit-test-components + ${project.version} + provided + true + + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + + true + + 1.6.8 + + + + \ No newline at end of file diff --git a/testing-support/uiunit-component-testers/src/main/java/com/vaadin/testbench/dontscan/noclassdeffound/NoClassDefFoundComponentTester.java b/testing-support/uiunit-component-testers/src/main/java/com/vaadin/testbench/dontscan/noclassdeffound/NoClassDefFoundComponentTester.java new file mode 100644 index 000000000..c535d84eb --- /dev/null +++ b/testing-support/uiunit-component-testers/src/main/java/com/vaadin/testbench/dontscan/noclassdeffound/NoClassDefFoundComponentTester.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2000-2025 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.testbench.dontscan.noclassdeffound; + +import com.vaadin.testbench.unit.ComponentTester; +import com.vaadin.testbench.unit.Tests; + +/** + * Dummy tester referencing a class from a third-party JAR that might not be + * available at runtime. This is used to verify that scanning for testers does + * not fail when the referenced component class is not on the classpath. + */ +@Tests(NoClassDefFoundComponent.class) +public class NoClassDefFoundComponentTester + extends ComponentTester { + + public NoClassDefFoundComponentTester(T component) { + super(component); + } +} diff --git a/testing-support/uiunit-component-testers/src/main/java/com/vaadin/testbench/dontscan/typenotpresent/TypeNotPresentComponentTester.java b/testing-support/uiunit-component-testers/src/main/java/com/vaadin/testbench/dontscan/typenotpresent/TypeNotPresentComponentTester.java new file mode 100644 index 000000000..c5d8d7a94 --- /dev/null +++ b/testing-support/uiunit-component-testers/src/main/java/com/vaadin/testbench/dontscan/typenotpresent/TypeNotPresentComponentTester.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2000-2025 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.testbench.dontscan.typenotpresent; + +import com.vaadin.flow.component.Component; +import com.vaadin.testbench.unit.ComponentTester; +import com.vaadin.testbench.unit.Tests; + +/** + * Dummy tester whose annotation references a class from a third-party JAR that + * might not be available at runtime. This is used to verify that scanning for + * testers does not fail when the referenced component class is not on the + * classpath. + */ +@Tests(TypeNotPresentComponent.class) +public class TypeNotPresentComponentTester extends ComponentTester { + + public TypeNotPresentComponentTester(Component component) { + super(component); + } +} diff --git a/testing-support/uiunit-test-components/pom.xml b/testing-support/uiunit-test-components/pom.xml new file mode 100644 index 000000000..345b5aeba --- /dev/null +++ b/testing-support/uiunit-test-components/pom.xml @@ -0,0 +1,50 @@ + + + + + 4.0.0 + + com.vaadin + vaadin-testbench-parent + 10.0-SNAPSHOT + ../../pom.xml + + + uiunit-test-components + jar + + + true + + + + + com.vaadin + flow-server + ${flow.version} + provided + + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + + true + + 1.6.8 + + + + \ No newline at end of file diff --git a/testing-support/uiunit-test-components/src/main/java/com/vaadin/testbench/dontscan/noclassdeffound/NoClassDefFoundComponent.java b/testing-support/uiunit-test-components/src/main/java/com/vaadin/testbench/dontscan/noclassdeffound/NoClassDefFoundComponent.java new file mode 100644 index 000000000..285827552 --- /dev/null +++ b/testing-support/uiunit-test-components/src/main/java/com/vaadin/testbench/dontscan/noclassdeffound/NoClassDefFoundComponent.java @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2000-2025 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ + +package com.vaadin.testbench.dontscan.noclassdeffound; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.Tag; + +@Tag("span") +public class NoClassDefFoundComponent extends Component { +} diff --git a/testing-support/uiunit-test-components/src/main/java/com/vaadin/testbench/dontscan/typenotpresent/TypeNotPresentComponent.java b/testing-support/uiunit-test-components/src/main/java/com/vaadin/testbench/dontscan/typenotpresent/TypeNotPresentComponent.java new file mode 100644 index 000000000..92fb41b24 --- /dev/null +++ b/testing-support/uiunit-test-components/src/main/java/com/vaadin/testbench/dontscan/typenotpresent/TypeNotPresentComponent.java @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2000-2025 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ + +package com.vaadin.testbench.dontscan.typenotpresent; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.Tag; + +@Tag("span") +public class TypeNotPresentComponent extends Component { +} diff --git a/vaadin-testbench-unit-junit5/pom.xml b/vaadin-testbench-unit-junit5/pom.xml index e16a948bd..6dcbc33d9 100644 --- a/vaadin-testbench-unit-junit5/pom.xml +++ b/vaadin-testbench-unit-junit5/pom.xml @@ -324,6 +324,17 @@ provided true + + org.slf4j + slf4j-simple + test + + + com.vaadin + uiunit-component-testers + ${project.version} + test + diff --git a/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/flow/component/charts/ChartTesterTest.java b/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/flow/component/charts/ChartTesterTest.java index 53432ef16..000a741e8 100644 --- a/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/flow/component/charts/ChartTesterTest.java +++ b/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/flow/component/charts/ChartTesterTest.java @@ -13,6 +13,7 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; +import com.vaadin.testbench.unit.CommercialTesterWrappers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -24,7 +25,7 @@ import com.vaadin.testbench.unit.ViewPackages; @ViewPackages -class ChartTesterTest extends UIUnitTest { +class ChartTesterTest extends UIUnitTest implements CommercialTesterWrappers { ColumnChartView view; diff --git a/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/testbench/unit/TesterScanTest.java b/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/testbench/unit/TesterScanTest.java new file mode 100644 index 000000000..b636ba7e8 --- /dev/null +++ b/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/testbench/unit/TesterScanTest.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2000-2025 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.Assertions; +import org.junit.jupiter.api.Test; + +// Ensure that scanning testers does not fail when a tester references +// a component class that is not present on the classpath. +public class TesterScanTest { + + @Test + public void scanForTesters_testerForClassNotInClasspath_doNotThrowOnClassNotFoundException() { + // Loads a dummy tester annotated with @Tests using an FQN to a + // non-existing component class. + Assertions.assertDoesNotThrow(() -> BaseUIUnitTest + .scanForTesters("com.vaadin.testbench.dontscan.classnotfound")); + } + + @Test + public void scanForTesters_testerForClassNotInClasspath_doNotThrowNoClassDefFound() { + // Loads a dummy tester annotated with @Tests referencing a class in + // another module with provided scope so the test itself is not able to + // load the class. + Assertions.assertDoesNotThrow(() -> BaseUIUnitTest.scanForTesters( + "com.vaadin.testbench.dontscan.noclassdeffound")); + } + + @Test + public void scanForTesters_testerForClassNotInClasspath_doNotThrowTypeNotPresentException() { + // Loads a dummy tester annotated with @Tests referencing a class in + // another module with provided scope so the test itself is not able to + // load the class. + Assertions.assertDoesNotThrow(() -> BaseUIUnitTest.scanForTesters( + "com.vaadin.testbench.dontscan.typenotpresent")); + } + +} diff --git a/vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/BaseUIUnitTest.java b/vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/BaseUIUnitTest.java index 0e7be4209..ccb1fa960 100644 --- a/vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/BaseUIUnitTest.java +++ b/vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/BaseUIUnitTest.java @@ -28,6 +28,7 @@ import io.github.classgraph.ClassGraph; import io.github.classgraph.ClassInfoList; import io.github.classgraph.ScanResult; +import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.vaadin.flow.component.Component; @@ -63,6 +64,8 @@ */ public abstract class BaseUIUnitTest { + private static final Logger LOGGER = LoggerFactory + .getLogger(BaseUIUnitTest.class.getPackageName()); private static final ConcurrentHashMap routesCache = new ConcurrentHashMap<>(); protected static final Map, Class> testers = new HashMap<>(); @@ -85,7 +88,8 @@ public abstract class BaseUIUnitTest { Capabilities.of(Capability.PRE_TRIAL)); } - private static Map, Class> scanForTesters( + // Visible for test + static Map, Class> scanForTesters( String... packages) { try (ScanResult scan = new ClassGraph().enableClassInfo() .enableAnnotationInfo().acceptPackages(packages).scan(2)) { @@ -114,19 +118,39 @@ private static Map, Class> scanForTesters( try { return UtilsKt.findClassOrThrow(clazz); } catch (ClassNotFoundException e) { - throw new RuntimeException(e); + logTypeLoadingIssue(e, + "Tester '{}' cannot be loaded because of missing component class '{}' on classpath", + classInfo.getName(), clazz); } - }).forEach(clazz -> testerMap.put(clazz, - (Class) tester)); - - } catch (ClassNotFoundException e) { - throw new RuntimeException(e); + return null; + }).filter(Objects::nonNull) + .forEach(clazz -> testerMap.put(clazz, + (Class) tester)); + + } catch (TypeNotPresentException e) { + logTypeLoadingIssue(e, + "Tester '{}' cannot be loaded because of missing class '{}' on classpath", + classInfo.getName(), e.typeName()); + } catch (ClassNotFoundException + | NoClassDefFoundError e) { + logTypeLoadingIssue(e, + "Tester '{}' cannot be loaded because of missing class on classpath: {}", + classInfo.getName(), e.getMessage()); } }); return Collections.unmodifiableMap(testerMap); } } + private static void logTypeLoadingIssue(Throwable ex, String message, + Object... args) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(message, args, ex); + } else { + LOGGER.warn(message, args); + } + } + protected synchronized Routes discoverRoutes() { return discoverRoutes(scanPackages()); } diff --git a/vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/CommercialTestWrappers.java b/vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/CommercialTesterWrappers.java similarity index 94% rename from vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/CommercialTestWrappers.java rename to vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/CommercialTesterWrappers.java index 5541ee504..2baf581c9 100644 --- a/vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/CommercialTestWrappers.java +++ b/vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/CommercialTesterWrappers.java @@ -16,7 +16,7 @@ * Provides factory method to create testers for commercial components. */ @SuppressWarnings("unchecked") -public interface CommercialTestWrappers { +public interface CommercialTesterWrappers { /** * Create a tester for the given Chart instance. diff --git a/vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/TesterWrappers.java b/vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/TesterWrappers.java index 8680fac39..b26711daa 100644 --- a/vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/TesterWrappers.java +++ b/vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/TesterWrappers.java @@ -14,8 +14,6 @@ import com.vaadin.flow.component.accordion.AccordionTester; import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.button.ButtonTester; -import com.vaadin.flow.component.charts.Chart; -import com.vaadin.flow.component.charts.ChartTester; import com.vaadin.flow.component.checkbox.Checkbox; import com.vaadin.flow.component.checkbox.CheckboxGroup; import com.vaadin.flow.component.checkbox.CheckboxGroupTester; @@ -138,18 +136,6 @@ default ButtonTester