From 637f74a725ab13008dc22eb3525c7e726c352902 Mon Sep 17 00:00:00 2001 From: Artur Signell Date: Tue, 24 Feb 2026 22:50:05 +0200 Subject: [PATCH 1/2] fix: Make ClassGraph discover package-private @Route views ClassGraph's default scan only returns public classes. Add ignoreClassVisibility() to the route auto-discovery scan so package-private views annotated with @Route are found. --- .../src/main/kotlin/com/vaadin/browserless/internal/Routes.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/browserless-test/shared/src/main/kotlin/com/vaadin/browserless/internal/Routes.kt b/browserless-test/shared/src/main/kotlin/com/vaadin/browserless/internal/Routes.kt index b1c6727d6..1a49c3a31 100644 --- a/browserless-test/shared/src/main/kotlin/com/vaadin/browserless/internal/Routes.kt +++ b/browserless-test/shared/src/main/kotlin/com/vaadin/browserless/internal/Routes.kt @@ -76,6 +76,7 @@ data class Routes( fun autoDiscoverViews(vararg packageNames: String? = arrayOf()): Routes = apply { val classGraph: ClassGraph = ClassGraph().enableClassInfo() .enableAnnotationInfo() + .ignoreClassVisibility() .acceptPackages(*(packageNames.map { it ?: "" }.toTypedArray())) classGraph.scan().use { scanResult: ScanResult -> scanResult.getClassesWithAnnotation(Route::class.java.name).mapTo(routes) { info: ClassInfo -> From 3be36cbb80ae5688a688c3bc8fc02da253748225 Mon Sep 17 00:00:00 2001 From: Artur Signell Date: Wed, 25 Feb 2026 06:58:23 +0000 Subject: [PATCH 2/2] test: Add test for package-private @Route view discovery Adds a package-private view (PackagePrivateView) and a dedicated test to verify that autoDiscoverViews() finds non-public @Route views. --- .../com/example/base/PackagePrivateView.java | 30 +++++++++++++++++++ .../vaadin/browserless/internal/RoutesTest.kt | 12 ++++++-- 2 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 browserless-test/junit6/src/test/java/com/example/base/PackagePrivateView.java diff --git a/browserless-test/junit6/src/test/java/com/example/base/PackagePrivateView.java b/browserless-test/junit6/src/test/java/com/example/base/PackagePrivateView.java new file mode 100644 index 000000000..bf2957198 --- /dev/null +++ b/browserless-test/junit6/src/test/java/com/example/base/PackagePrivateView.java @@ -0,0 +1,30 @@ +/** + * Copyright (C) 2000-2026 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. + */ +package com.example.base; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.router.Route; + +/** + * A package-private route view used to verify that + * {@link com.vaadin.browserless.internal.Routes#autoDiscoverViews} + * discovers non-public views. + */ +@Tag("div") +@Route("package-private") +class PackagePrivateView extends Component { +} diff --git a/browserless-test/junit6/src/test/kotlin/com/vaadin/browserless/internal/RoutesTest.kt b/browserless-test/junit6/src/test/kotlin/com/vaadin/browserless/internal/RoutesTest.kt index 26be849bf..a320f1212 100644 --- a/browserless-test/junit6/src/test/kotlin/com/vaadin/browserless/internal/RoutesTest.kt +++ b/browserless-test/junit6/src/test/kotlin/com/vaadin/browserless/internal/RoutesTest.kt @@ -33,10 +33,11 @@ import com.vaadin.browserless.viewscan.byannotatedclass.ViewPackagesTestView import kotlin.test.expect +val packagePrivateViewClass: Class = Class.forName("com.example.base.PackagePrivateView").asSubclass(Component::class.java) val allViews: Set> = setOf>( HelloWorldView::class.java, WelcomeView::class.java, ParametrizedView::class.java, ChildView::class.java, NavigationPostponeView::class.java, - ViewPackagesTestView::class.java, SignalsView::class.java) + ViewPackagesTestView::class.java, SignalsView::class.java, packagePrivateViewClass) val allErrorRoutes: Set>> = setOf(ErrorView::class.java, MockRouteNotFoundError::class.java, MockInternalSeverError::class.java) @DynaTestDsl @@ -53,6 +54,13 @@ fun DynaNodeGroup.routesTestBatch() { expect(allErrorRoutes) { routes.errorRoutes.toSet() } } + test("package-private views are discovered") { + val routes: Routes = Routes().autoDiscoverViews(*packagesToScan) + expect(true, "PackagePrivateView should be discovered") { + routes.routes.any { it.name == "com.example.base.PackagePrivateView" } + } + } + test("calling autoDiscoverViews() multiple times won't fail") { expect(allViews) { Routes().autoDiscoverViews(*packagesToScan).routes } expect(allViews) { Routes().autoDiscoverViews(*packagesToScan).routes } @@ -106,7 +114,7 @@ fun DynaNodeGroup.routesTestBatch() { test("merge routes") { val routes1 = Routes(mutableSetOf(HelloWorldView::class.java, WelcomeView::class.java, - ViewPackagesTestView::class.java, SignalsView::class.java), + ViewPackagesTestView::class.java, SignalsView::class.java, packagePrivateViewClass), mutableSetOf(ErrorView::class.java)) val routes2 = Routes(mutableSetOf(ParametrizedView::class.java, ChildView::class.java, NavigationPostponeView::class.java), mutableSetOf(MockRouteNotFoundError::class.java, MockInternalSeverError::class.java))