diff --git a/.gitignore b/.gitignore
index 0be38590..91cf2899 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
target
spotless-index
.since-index
+*.iml
+.idea
diff --git a/CLAUDE.md b/CLAUDE.md
index 67522f20..2e208bf4 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -17,7 +17,6 @@ each Vaadin component in a *tester* that drives it the way a browser would.
- Java 21+, Maven (multi-module)
- Vaadin 25 / Flow — a `provided` dependency, one fixed version per branch
-- Kotlin for the older mock and internal layer in `shared/src/main/kotlin`
- JUnit 6 (Jupiter) for the test API and for this repository's own tests
- An annotation processor (`locator-processor`) that generates the typed
locator API at build time
@@ -84,7 +83,7 @@ mvn test -pl junit6 -Dtest=BasicGridTesterTest#basicGrid_selectionOnClick
# Run the tests matching a pattern
mvn test -pl junit6 -Dtest="*ComboBox*Test"
-# Generate the Javadoc/Dokka artifacts the way CI does
+# Generate the Javadoc artifacts the way CI does
mvn clean install -DskipTests -Djavadocs
```
diff --git a/CONVENTIONS.md b/CONVENTIONS.md
index 58769be8..5092e3cb 100644
--- a/CONVENTIONS.md
+++ b/CONVENTIONS.md
@@ -112,9 +112,7 @@ Go through the Vaadin extension points rather than around them — fire service
and session lifecycle events through the `VaadinService` event bus, and forward
`Instantiator` calls to the real instantiator instead of reimplementing them.
-New code in `shared` is written in Java. The Kotlin sources under
-`shared/src/main/kotlin` are the older mock and internal layer and are being
-ported to Java; do not add new Kotlin files there.
+This repository is written in Java; do not add Kotlin sources.
See [`guidelines/architecture.md`](guidelines/architecture.md).
@@ -152,6 +150,13 @@ detail.
Javadoc describes the code today, not what changed. Change history belongs in
commit messages.
+In a tester, spell cross-package `@link` and `@throws` targets out in full:
+`locator-processor` copies the Javadoc into the generated `*Locator`, which has
+no imports. Every module builds clean under doclint, warnings included, so every
+published member needs a description, its `@param`s and its `@return` — a class
+that would otherwise get an undocumented default constructor declares an
+explicit one.
+
See [`guidelines/documenting.md`](guidelines/documenting.md).
## Testing
diff --git a/de-kotlin-plan.md b/de-kotlin-plan.md
new file mode 100644
index 00000000..f69e7fa2
--- /dev/null
+++ b/de-kotlin-plan.md
@@ -0,0 +1,499 @@
+# Migrating the Kotlin sources to plain Java
+
+Working plan for removing Kotlin from `browserless-test`. The goal is that
+`shared/src/main` is 100% Java and that downstream consumers of
+`browserless-test-shared` pull no Kotlin runtime transitively.
+
+## Current state
+
+| Area | Files | LOC | Notes |
+| --- | --- | --- | --- |
+| `shared/src/main/kotlin/…/mocks` | 12 | 1,721 | servlet API mocks, `MockService`, `MockedUI` |
+| `shared/src/main/kotlin/…/internal` | 12 | 3,043 | `MockVaadin`, `Locator`, `Routes`, `PrettyPrintTree`, utilities |
+| `shared/src/main/kotlin/…/component/Grid.kt` | 1 | 901 | the heaviest single file |
+| `junit6/src/test/kotlin` | 23 | ~3,050 | DynaTest + Karibu DSL |
+| `shared/src/test/kotlin` | 2 | 144 | DynaTest |
+
+All of it is ported as of Phase 6; the table is the starting point the phases
+below work through.
+
+`spring`, `quarkus`, `junit6/src/main` and `locator-processor` are already pure
+Java, but about 48 Java files reference the Kotlin packages and 24 call sites go
+through Kotlin file facades (`LocatorKt`, `PrettyPrintTreeKt`, `GridKt`,
+`UtilsKt`, `BasicUtilsKt`, `ShortcutsKt`).
+
+## Prior art
+
+`origin/feat/no-kotlin-grid` carries a complete five-commit port of all 25
+main-source Kotlin files, verified green on all four suites
+(20 / 1007 / 29 / 27 tests). It branched from `a333ccc` and is 82 commits behind
+`main`, so it is a source to rebase from, not a branch to merge.
+
+Reviewer notes from that effort live on the branch in `de-kotlin-review/`.
+
+Drift to reconcile while rebasing — most files differ only by the
+commercial → Apache license header swap (`08abcfa`); the real drift is:
+
+| File | Drift | Why |
+| --- | --- | --- |
+| `internal/MockVaadin.kt` | +222 | reload / window-name plumbing, `liveUI`, `fireSessionDestroyAndDrain`, multi-user session objects |
+| `mocks/MockInstantiator.kt` | +118 | deprecated, forwarders hand-unrolled |
+| `internal/PrettyPrintTree.kt` | +93 | `hrefValue()` already rewritten in plain Java reflection — expect a conflict with Phase 3 |
+| `component/Grid.kt` | +86 | selection and click additions |
+| `mocks/MockedUI.kt` | +82 | `navigate()` override and `toLocation()` |
+| `internal/TestingLifecycleHook.kt` | +60 | slot / children rules |
+| `mocks/MockRequest.kt` | +60 | role checker, principal provider |
+| `mocks/MockHttpSession.kt` | +39 | `changeSessionId()` |
+
+`df099d0` already dropped `kotlin-reflect` on `main`, so that dependency win is
+banked independently of this work.
+
+## Phases
+
+Each phase is independently mergeable and leaves the build green.
+
+### Phase 0 — prerequisites
+
+Decisions that constrain later phases:
+
+- **Nullability annotations.** 11 Java files in `shared`, `spring` and
+ `quarkus` import `org.jetbrains.annotations.@NotNull` / `@Nullable`, which
+ arrives transitively via `kotlin-stdlib`. Move them to JSpecify (already used
+ in 5 files) or add a direct `org.jetbrains:annotations` dependency. Without
+ this the build breaks the moment `kotlin-stdlib` leaves compile scope.
+- **`SearchSpec.count`.** Pick the replacement for `kotlin.ranges.IntRange`
+ (the prior effort introduced a small `CountRange` helper). This constrains
+ Phases 4 and 5.
+- **Test-side Kotlin.** In or out of scope — see below.
+
+### Phase 1 — `mocks/` — done
+
+12 Kotlin files → 14 Java files, on `refactor/no-kotlin-mocks`. All five suites
+match the `main` baseline exactly: shared 42, junit6 1406, junit6-cdi-tests 4,
+spring 41, quarkus 31.
+
+- `MockHttpEnvironment.kt` splits into `MockHttpEnvironment`,
+ `MockServletConfig` and a `MockUtils.putOrRemove` helper.
+- `MockVaadinServlet.kt`'s top-level factories (`serviceSafe`,
+ `createVaadinServletRequest` / `…Response`, `_createVaadinSession`,
+ `WebBrowser(request)`) become statics on `MockVaadinServlet`. The last one is
+ renamed `createWebBrowser` to avoid clashing with
+ `com.vaadin.flow.server.WebBrowser`.
+- `SessionAttributeMap` becomes a public class; the `HttpSession.attributes`
+ extension property disappears and callers construct it directly.
+- `MockInstantiator`'s `Instantiator by delegate` is unrolled by hand — see the
+ silent-risk list below.
+- `MockRequest` keeps an explicit `setUserInRole(BiPredicate)` setter, matching
+ what Kotlin's `is`-prefix property convention emitted.
+- `MockVaadinServlet.createServletService` now declares
+ `throws ServiceException`, as its `VaadinServlet` superclass does — see the
+ breaking changes below.
+- KDoc is rewritten as Javadoc rather than carried over verbatim: `[Foo]`
+ becomes `{@link Foo}`, `*` bullets become `
`, and every public member
+ gets `@param` / `@return`. A mock's class Javadoc states which container
+ behavior it reproduces and where it stops, per
+ [`guidelines/documenting.md`](guidelines/documenting.md). Once Dokka is gone
+ (Phase 5) this is what `maven-javadoc-plugin` publishes.
+
+### Phase 2 — `internal/` utilities — done
+
+`BasicUtils`, `ComponentUtils`, `ElementUtils`, `DepthFirstTreeIterator`,
+`Renderers`, `Shortcuts`, `TestingLifecycleHook`, `Utils`, on
+`refactor/no-kotlin-internal-utils`. All five suites match the `main` baseline:
+shared 42, junit6 1406, junit6-cdi-tests 4, spring 41, quarkus 31.
+
+One Java utility class per Kotlin file: `public final`, private constructor,
+top-level and extension functions become `public static` methods with the
+receiver as the first parameter. The leading-underscore convention
+(`_fireEvent`, `_isVisible`, `_saneFetchLimit`) is preserved.
+
+`TestingLifecycleHook` splits into the interface plus a `TestingLifecycleHooks`
+holder, because a Java interface cannot hold the mutable global that the Kotlin
+top-level `var testingLifecycleHook` provided. The global is a
+`getCurrent()` / `setCurrent(…)` pair, not a public field — see the
+`MockHttpEnvironment` lesson from Phase 1.
+
+A 20-line `Matches.kt` shim stays behind, holding `Component.matches(…)` and
+`IntRange.size`. Both take Kotlin-only types and go away with `SearchSpec`
+(Phase 4) and `Grid` (Phase 5).
+
+Two things the drift check caught, both in `TestingLifecycleHook`: its
+`getAllChildren` had a `Grid` branch that was commented out when the prior port
+was written and is live on `main`, and its fallback moved from
+`_getVirtualChildren` to `ComponentUtil.getAllChildren`. Porting the old Java as
+written would have silently reverted both.
+
+### Phase 3 — `PrettyPrintTree` + `Routes` — done
+
+On `refactor/no-kotlin-pretty-routes`. All five suites match the `main`
+baseline: shared 42, junit6 1406, junit6-cdi-tests 4, spring 41, quarkus 31.
+
+`MockRouteNotFoundError` and `MockInternalSeverError` move to their own files
+(Java allows one public class per file). `main`'s `hrefValue()` had already been
+rewritten in plain Java reflection, and it is considerably more thorough than
+what the prior port carried — it walks the class hierarchy reading declared
+methods *and* fields at any visibility, because `Anchor` keeps its `href` in a
+private field. That version is the one to port; the prior port's getter-only
+lookup would have quietly changed what a tree dump shows for an `Anchor`.
+
+`TreeOnFailureExtension` (`PrettyPrintTree.Companion.ofVaadin`) is fixed here.
+Nothing else referenced it, and the line only runs on test failure, so a green
+suite does not catch it.
+
+`kotlin-reflect` was already dropped on `main` by `df099d0`, so this phase has
+no dependency change.
+
+### Phase 4 — `Locator` + `MockVaadin` — done
+
+On `refactor/no-kotlin-locator-mockvaadin`. All five suites match the `main`
+baseline: shared 42, junit6 1406, junit6-cdi-tests 4, spring 41, quarkus 31.
+After this phase no Java source in any module references a Kotlin type.
+
+- `Locator.kt` → `Locator` + `SearchSpec`.
+- `MockVaadin.kt` → `MockVaadin` + `SessionObjects` + `UIFactory` +
+ `MockRequestCustomizer` + `MockPage`.
+- `runUIQueue` needs the `sneakyThrow` idiom, or `AsyncTest`'s
+ `expectThrows(ExecutionException)` fails — Kotlin rethrows arbitrary
+ throwables where Java cannot.
+- Keep `UIFactory`'s SAM method named `invoke()` so `MockedUI::new` call sites
+ and the Spring / Quarkus constructors keep binding.
+- `CountRange` replaces `kotlin.ranges.IntRange` here rather than in Phase 5, so
+ `SearchSpec` and `ComponentQuery.LocatorSpec` are edited once instead of
+ twice.
+- The Kotlin DSL shim goes straight to `junit6/src/test/kotlin` as
+ `LocatorDsl.kt`, skipping the prior effort's intermediate stop in
+ `shared/src/main/kotlin`. Nothing in `shared/src/test` uses the locator DSL.
+- The three `@Deprecated(forRemoval = true)` Spring constructors taking
+ `Function0` are removed, as agreed: with a plain Java `UIFactory` they are
+ ambiguous against the `UIFactory` overload at any `MockedUI::new` call site.
+- `MockVaadin` fires session-init, service-destroy and UI-init through
+ `VaadinService.getEventBus()` with a rethrowing failure handler, as `main`
+ does. The prior port reflected into `VaadinService`'s private listener
+ collections and swallowed the rethrow, which both
+ [`CONVENTIONS.md`](CONVENTIONS.md) and
+ [`guidelines/architecture.md`](guidelines/architecture.md) argue against.
+
+Two things the drift check caught. `SearchSpec` had no `testId` at all, because
+that field postdates the prior port — it would have broken the public
+`ComponentQuery.withTestId(…)`. And `MockPage.reload()` was missing the
+logout-idiom early return, the wrong-UI guard and the
+`recordReloadReplacement(…)` call, which six reload tests caught.
+
+### Phase 5 — `Grid.kt` and the kotlin-stdlib drop — done
+
+On `refactor/no-kotlin-grid`. All five suites match the `main` baseline:
+shared 42, junit6 1406, junit6-cdi-tests 4, spring 41, quarkus 31.
+`shared/src/main` is now 100% Java and `kotlin-stdlib` is off its compile
+classpath, so downstream consumers pull no Kotlin runtime.
+
+- `Sequence` returns (`_rowSequence`) become `Stream` built over
+ `DepthFirstTreeIterator` + `Spliterator`. Laziness matters: `TreeGrid._size()`
+ is `_rowSequence().count()`.
+- The `KProperty1` overloads of `HeaderRow.getCell` / `FooterRow.getCell` have
+ no callers — drop them; the `getCell(String)` overloads stay.
+- Delete `shared/src/main/kotlin`, move `kotlin-stdlib` to test scope, replace
+ Dokka with `maven-javadoc-plugin`.
+- `_dump(Grid, IntRange)` becomes `_dump(Grid, int from, int to)`. A row range
+ is not a count, so it needs no `CountRange`.
+- The `org.jetbrains.annotations` imports move to JSpecify, which Vaadin already
+ puts on the classpath at `provided` scope — no new dependency.
+
+**Replacing Dokka turns Javadoc validation on for the first time.** Dokka
+validated nothing, so `maven-javadoc-plugin` fails `shared` the moment it
+replaces it. `shared` carried `none` through Phase 5 so the
+port would not turn into a module-wide Javadoc cleanup; see
+[Phase 7](#phase-7--the-doclint-cleanup--done) for that cleanup.
+
+### Phase 6 — test-side Kotlin — done
+
+The last 25 Kotlin files, all test-scoped, are now JUnit 6 Java, and the word
+Kotlin is gone from the build: no `kotlin-maven-plugin`, no `dynatest`, no
+`karibu-dsl`, no `kotlin-stdlib`, no Dokka, no `kotlin` block in Spotless. All
+five suites match the `main` baseline: shared 42, junit6 1406,
+junit6-cdi-tests 4, spring 41, quarkus 31.
+
+- `group { … }` / `test { … }` map onto `@Nested` / `@Test`; `beforeEach` and
+ `afterEach` onto `@BeforeEach` / `@AfterEach`, and a `beforeGroup` onto
+ `@BeforeAll`. `AllTests.kt` was a pure aggregator: its two standalone tests
+ became `TestClasspathTest` and the rest of it disappeared.
+- `locatorTest()` and `locatorTest2()` were two DynaTest fragments the
+ aggregator wired under different lifecycle hooks, so they became two classes:
+ `LocatorTest` (with `MyLifecycleHook` installed) and
+ `LocatorWithoutLifecycleHookTest`.
+- `LocatorDsl.kt` existed only to keep `_get
* The class is intentionally opened, to be extensible in user's library.
*
+ *
+ * For internal use only. May be renamed or removed in a future release.
+ *
* @since 1.0
*/
public class MockQuarkusServletService extends QuarkusVaadinServletService {
diff --git a/shared/pom.xml b/shared/pom.xml
index cb432d2b..f77ec17a 100644
--- a/shared/pom.xml
+++ b/shared/pom.xml
@@ -15,103 +15,31 @@
-
-
- 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.pluginsmaven-compiler-plugin
-
-
-
- default-compile
- none
-
-
-
- default-testCompile
- none
-
-
- java-compile
- compile
-
- compile
-
-
-
-
- com.vaadin
- browserless-test-locator-processor
- ${project.version}
-
-
-
-
-
- java-test-compile
- test-compile
-
- testCompile
-
-
-
+
+
+
+ com.vaadin
+ browserless-test-locator-processor
+ ${project.version}
+
+
+
-
+
- org.jetbrains.dokka
- dokka-maven-plugin
+ org.apache.maven.plugins
+ maven-javadoc-pluginorg.codehaus.mojobuild-helper-maven-plugin
-
- add-kotlin-sources-for-source-jar
- package
-
- add-source
-
-
-
- src/main/kotlin
-
-
- add-generated-locator-sourcesgenerate-sources
@@ -170,11 +98,14 @@
test
+
- org.jetbrains.kotlin
- kotlin-stdlib
- ${kotlin.version}
+ org.junit.jupiter
+ junit-jupiter
+ test
+
io.github.classgraphclassgraph
@@ -196,13 +127,6 @@
true
-
- com.github.mvysny.dynatest
- dynatest
- 0.25
- test
-
-
jakarta.servletjakarta.servlet-api
diff --git a/shared/src/main/java/com/vaadin/browserless/BaseBrowserlessTest.java b/shared/src/main/java/com/vaadin/browserless/BaseBrowserlessTest.java
index e5bb70c0..9a8a3df3 100644
--- a/shared/src/main/java/com/vaadin/browserless/BaseBrowserlessTest.java
+++ b/shared/src/main/java/com/vaadin/browserless/BaseBrowserlessTest.java
@@ -53,10 +53,21 @@
*/
public abstract class BaseBrowserlessTest {
+ /**
+ * Creates the test base; subclasses are instantiated by the test engine.
+ */
+ protected BaseBrowserlessTest() {
+ }
+
private TestSignalEnvironment signalsTestEnvironment;
private BrowserlessConfiguration resolvedConfiguration;
private boolean classScopedConfiguration;
+ /**
+ * Equivalent to {@code discoverRoutes(scanPackages())}.
+ *
+ * @return the routes found in the scanned packages
+ */
protected synchronized Routes discoverRoutes() {
return discoverRoutes(scanPackages());
}
@@ -64,6 +75,8 @@ protected synchronized Routes discoverRoutes() {
/**
* Discover and return Routes for mocked Vaadin core system.
*
+ * @param packageNames
+ * the packages to scan for routes
* @see #initVaadinEnvironment()
* @return Routes
*/
@@ -129,6 +142,10 @@ protected final Set> allLookupServices(
return services;
}
+ /**
+ * Registers the signals test environment, so that signal-backed components
+ * work inside the mocked environment.
+ */
protected void initSignalsSupport() {
signalsTestEnvironment = TestSignalEnvironment.register();
}
@@ -146,6 +163,12 @@ protected void scanTesters() {
}
}
+ /**
+ * Collects the packages to scan for routes, from the {@code @ViewPackages}
+ * annotation on the test class.
+ *
+ * @return the packages to scan, empty to scan the whole classpath
+ */
protected Set scanPackages() {
Set packagesToScan = new HashSet<>();
@@ -284,10 +307,10 @@ void setResolvedConfiguration(BrowserlessConfiguration configuration,
/**
* Navigate to the given view class if it is registered.
*
- * @param navigationTarget
- * view class to navigate to
* @param
* view type
+ * @param navigationTarget
+ * view class to navigate to
* @return instantiated view
*/
public T navigate(Class navigationTarget) {
@@ -297,14 +320,14 @@ public T navigate(Class navigationTarget) {
/**
* Navigate to view with url parameter.
*
+ * @param
+ * parameter type
+ * @param
+ * view type
* @param navigationTarget
* view class to navigate to
* @param parameter
* parameter to send to view
- * @param
- * view type
- * @param
- * parameter type
* @return instantiated view
*/
public > T navigate(
@@ -322,13 +345,13 @@ public > T navigate(
* with a query string, write it into the location given to
* {@link #navigate(String, Class)}.
*
+ * @param
+ * view type
* @param navigationTarget
* view class to navigate to
* @param parameters
* route parameters of the target's route template, keyed by
* parameter name
- * @param
- * view type
* @return instantiated view
*/
public T navigate(Class navigationTarget,
@@ -346,13 +369,13 @@ public T navigate(Class navigationTarget,
* {@code "order/ORD-1?tab=history"}, whose query parameters the view reads
* from the navigation event.
*
+ * @param
+ * view type
* @param location
* location string for navigating, optionally with a query string
* and a fragment
* @param expectedTarget
* class that is expected for navigation
- * @param
- * view type
* @return instantiated view
*/
public T navigate(String location,
@@ -380,10 +403,10 @@ public HasElement reload() {
* Simulates a page reload (see {@link #reload()}) and verifies the
* resulting view is of the expected type.
*
- * @param expectedTarget
- * the expected view class after reload
* @param
* the view type
+ * @param expectedTarget
+ * the expected view class after reload
* @return the view shown after the reload
* @since 25.4
*/
@@ -414,11 +437,36 @@ public HasElement getCurrentView() {
}
// Protected for access by adapter subclass in legacy module
+ /**
+ * Equivalent to {@code TesterRegistry.wrap(component)}.
+ *
+ * @param
+ * the item type
+ * @param component
+ * the component to wrap
+ * @return a tester for the given component
+ * @param
+ * the component type
+ */
protected static , Y extends Component> T internalWrap(
Y component) {
return TesterRegistry.wrap(component);
}
+ /**
+ * Wraps the component in the given tester type, preferring a more specific
+ * tester registered through {@code @Tests} when one exists.
+ *
+ * @param
+ * the tester type
+ * @param
+ * the component type
+ * @param wrap
+ * the tester type the caller asked for
+ * @param component
+ * the component to wrap
+ * @return a tester for the given component
+ */
@SuppressWarnings({ "unchecked", "rawtypes" })
protected static , Y extends Component> T internalWrap(
Class wrap, Y component) {
@@ -488,10 +536,10 @@ public , Y extends Component> T test(
* context menu, and {@code GridTester.contextMenu(row).open()} for a grid
* context menu.
*
- * @param componentType
- * the type of the component(s) to search for
* @param
* the type of the component(s) to search for
+ * @param componentType
+ * the type of the component(s) to search for
* @return a query object for finding components
* @since 1.1
*/
@@ -508,12 +556,12 @@ public ComponentQuery find(
* Searches the same server-side component tree as {@link #find(Class)}, see
* there for what that tree does not contain.
*
+ * @param
+ * the type of the component(s) to search for
* @param componentType
* the type of the component(s) to search for
* @param fromThis
* component used as starting element for search.
- * @param
- * the type of the component(s) to search for
* @return a query object for finding components
* @since 1.1
*/
@@ -529,10 +577,10 @@ public ComponentQuery find(Class componentType,
* Searches the same server-side component tree as {@link #find(Class)}, see
* there for what that tree does not contain.
*
- * @param componentType
- * the type of the component(s) to search for
* @param
* the type of the component(s) to search for
+ * @param componentType
+ * the type of the component(s) to search for
* @return a query object for finding components
* @since 1.1
*/
@@ -544,10 +592,10 @@ public ComponentQuery findInView(
/**
* Gets a query object for finding a component inside the UI.
*
- * @param componentType
- * the type of the component(s) to search for
* @param
* the type of the component(s) to search for
+ * @param componentType
+ * the type of the component(s) to search for
* @return a query object for finding components
* @deprecated since 1.1, for removal in 26.0; use {@link #find(Class)}
* instead.
@@ -561,12 +609,12 @@ public ComponentQuery findInView(
* Gets a query object for finding a component nested inside the given
* component.
*
+ * @param
+ * the type of the component(s) to search for
* @param componentType
* the type of the component(s) to search for
* @param fromThis
* component used as starting element for search.
- * @param
- * the type of the component(s) to search for
* @return a query object for finding components
* @deprecated since 1.1, for removal in 26.0; use
* {@link #find(Class, Component)} instead.
@@ -580,10 +628,10 @@ public ComponentQuery findInView(
/**
* Gets a query object for finding a component inside the current view.
*
- * @param componentType
- * the type of the component(s) to search for
* @param
* the type of the component(s) to search for
+ * @param componentType
+ * the type of the component(s) to search for
* @return a query object for finding components
* @deprecated since 1.1, for removal in 26.0; use
* {@link #findInView(Class)} instead.
diff --git a/shared/src/main/java/com/vaadin/browserless/BrowserlessConfiguration.java b/shared/src/main/java/com/vaadin/browserless/BrowserlessConfiguration.java
index 260bd3db..d8d03023 100644
--- a/shared/src/main/java/com/vaadin/browserless/BrowserlessConfiguration.java
+++ b/shared/src/main/java/com/vaadin/browserless/BrowserlessConfiguration.java
@@ -55,8 +55,17 @@ public final class BrowserlessConfiguration implements Serializable {
private static final BrowserlessConfiguration EMPTY = new BrowserlessConfiguration(
Map.of(), Map.of(), Set.of());
+ /**
+ * The application properties fed to the deployment configuration.
+ */
private final Map applicationProperties;
+ /**
+ * The feature flags to turn on or off, by feature id.
+ */
private final Map featureFlags;
+ /**
+ * The extra service classes handed to the lookup initializer.
+ */
private final Set> lookupServices;
private BrowserlessConfiguration(Map applicationProperties,
diff --git a/shared/src/main/java/com/vaadin/browserless/BrowserlessDSL.java b/shared/src/main/java/com/vaadin/browserless/BrowserlessDSL.java
index b8167a38..4c57427e 100644
--- a/shared/src/main/java/com/vaadin/browserless/BrowserlessDSL.java
+++ b/shared/src/main/java/com/vaadin/browserless/BrowserlessDSL.java
@@ -19,7 +19,7 @@
import java.util.concurrent.TimeUnit;
import com.vaadin.browserless.internal.MockInternalSeverError;
-import com.vaadin.browserless.internal.ShortcutsKt;
+import com.vaadin.browserless.internal.Shortcuts;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.HasElement;
import com.vaadin.flow.component.Key;
@@ -119,11 +119,10 @@ static ComponentQuery findView(UI ui,
static void fireShortcut(UI ui, Key key, KeyModifier... modifiers) {
if (ui.hasModalComponent()) {
- ShortcutsKt._fireShortcut(
- ui.getInternals().getActiveModalComponent(), key,
- modifiers);
+ Shortcuts._fireShortcut(ui.getInternals().getActiveModalComponent(),
+ key, modifiers);
} else {
- ShortcutsKt.fireShortcut(key, modifiers);
+ Shortcuts.fireShortcut(key, modifiers);
}
}
diff --git a/shared/src/main/java/com/vaadin/browserless/BrowserlessTestSetupException.java b/shared/src/main/java/com/vaadin/browserless/BrowserlessTestSetupException.java
index 5ce431ec..0304121b 100644
--- a/shared/src/main/java/com/vaadin/browserless/BrowserlessTestSetupException.java
+++ b/shared/src/main/java/com/vaadin/browserless/BrowserlessTestSetupException.java
@@ -22,10 +22,24 @@
* @since 1.0
*/
public class BrowserlessTestSetupException extends RuntimeException {
+ /**
+ * Creates an exception with the given message.
+ *
+ * @param message
+ * what went wrong while setting the test environment up
+ */
public BrowserlessTestSetupException(String message) {
super(message);
}
+ /**
+ * Creates an exception with the given message and cause.
+ *
+ * @param message
+ * what went wrong while setting the test environment up
+ * @param cause
+ * the failure this one wraps
+ */
public BrowserlessTestSetupException(String message, Throwable cause) {
super(message, cause);
}
diff --git a/shared/src/main/java/com/vaadin/browserless/BrowserlessUIContext.java b/shared/src/main/java/com/vaadin/browserless/BrowserlessUIContext.java
index 6c7760a2..33499ac5 100644
--- a/shared/src/main/java/com/vaadin/browserless/BrowserlessUIContext.java
+++ b/shared/src/main/java/com/vaadin/browserless/BrowserlessUIContext.java
@@ -21,7 +21,6 @@
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
-import com.vaadin.browserless.internal.MockPage;
import com.vaadin.browserless.internal.MockVaadin;
import com.vaadin.browserless.locator.Locators;
import com.vaadin.flow.component.Component;
@@ -550,7 +549,7 @@ public boolean runPendingSignalsTasks(long maxWaitTime, TimeUnit unit) {
*/
public String getExternalNavigationURL() {
activate();
- if (ui.getPage() instanceof MockPage mockPage) {
+ if (ui.getPage() instanceof MockVaadin.MockPage mockPage) {
return mockPage.getLastExternalNavigationURL();
}
return null;
@@ -568,7 +567,7 @@ public String getExternalNavigationURL() {
*/
public String getExternalNavigationURL(String windowName) {
activate();
- if (ui.getPage() instanceof MockPage mockPage) {
+ if (ui.getPage() instanceof MockVaadin.MockPage mockPage) {
return mockPage.getExternalNavigationURL(windowName);
}
return null;
@@ -590,7 +589,7 @@ public String getExternalNavigationURL(String windowName) {
*/
public Map> getOpenedWindows() {
activate();
- if (ui.getPage() instanceof MockPage mockPage) {
+ if (ui.getPage() instanceof MockVaadin.MockPage mockPage) {
return mockPage.getOpenedWindows();
}
return Map.of();
diff --git a/shared/src/main/java/com/vaadin/browserless/CommercialTesterWrappers.java b/shared/src/main/java/com/vaadin/browserless/CommercialTesterWrappers.java
index d711e4e4..513e9b09 100644
--- a/shared/src/main/java/com/vaadin/browserless/CommercialTesterWrappers.java
+++ b/shared/src/main/java/com/vaadin/browserless/CommercialTesterWrappers.java
@@ -33,6 +33,8 @@ public interface CommercialTesterWrappers {
/**
* Create a tester for the given GridPro instance.
*
+ * @param
+ * the value type
* @param grid
* the GridPro instance to be tested
* @return a GridProTester instance wrapping the given GridPro
@@ -45,6 +47,8 @@ default GridProTester, V> test(GridPro grid) {
/**
* Create a tester for the given GridPro instance.
*
+ * @param
+ * the value type
* @param grid
* the GridPro instance to be tested
* @param itemType
diff --git a/shared/src/main/java/com/vaadin/browserless/ComponentQuery.java b/shared/src/main/java/com/vaadin/browserless/ComponentQuery.java
index fb557db0..19827c16 100644
--- a/shared/src/main/java/com/vaadin/browserless/ComponentQuery.java
+++ b/shared/src/main/java/com/vaadin/browserless/ComponentQuery.java
@@ -25,10 +25,8 @@
import java.util.function.Predicate;
import java.util.stream.Stream;
-import kotlin.Unit;
-import kotlin.ranges.IntRange;
-
-import com.vaadin.browserless.internal.LocatorKt;
+import com.vaadin.browserless.internal.CountRange;
+import com.vaadin.browserless.internal.Locator;
import com.vaadin.browserless.internal.SearchSpec;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.shared.ThemeVariant;
@@ -77,6 +75,8 @@ public ComponentQuery(Class componentType) {
/**
* Requires the given property to have expected value.
*
+ * @param
+ * the value type
* @param getter
* the function to get the value of the property of the field,
* not null
@@ -101,6 +101,8 @@ public ComponentQuery withPropertyValue(Function getter,
* Providing a {@literal null} value as {@code expectedValue} has no effects
* since the filter will not be applied.
*
+ * @param
+ * the value type
* @param expectedValue
* value to be compared with the one obtained by
* {@link com.vaadin.flow.component.HasValue#getValue()}
@@ -122,7 +124,7 @@ public ComponentQuery withValue(V expectedValue) {
public ComponentQuery withId(String id) {
locatorSpec.id = id;
// At most one element with given id is expected
- locatorSpec.count = new IntRange(0, 1);
+ locatorSpec.count = new CountRange(0, 1);
return this;
}
@@ -142,7 +144,7 @@ public ComponentQuery withId(String id) {
public ComponentQuery withTestId(String testId) {
locatorSpec.testId = testId;
// At most one element with given test-id is expected
- locatorSpec.count = new IntRange(0, 1);
+ locatorSpec.count = new CountRange(0, 1);
return this;
}
@@ -464,7 +466,7 @@ public ComponentQuery withResultsSize(int count) {
"count must be greater or equal than zero, but was "
+ count);
}
- locatorSpec.count = new IntRange(count, count);
+ locatorSpec.count = CountRange.exactly(count);
return this;
}
@@ -495,7 +497,7 @@ public ComponentQuery withResultsSize(int min, int max) {
"max must be greater or equal than min, but was min=" + min
+ ", max=" + max + "");
}
- locatorSpec.count = new IntRange(min, max);
+ locatorSpec.count = new CountRange(min, max);
return this;
}
@@ -743,10 +745,10 @@ private boolean isInSlot(Component component, String slot) {
* Gets a new {@link ComponentQuery} to search for given component type on
* the context of first matching component for current query.
*
- * @param componentType
- * the type of the component(s) to search for
* @param
* the type of the component(s) to search for
+ * @param componentType
+ * the type of the component(s) to search for
* @return a new query object, to search for nested components.
* @throws java.util.NoSuchElementException
* if first component is found
@@ -764,10 +766,12 @@ public ComponentQuery thenOnFirst(
* the actual number of components found results in an
* {@link IndexOutOfBoundsException}.
*
- * @param componentType
- * the type of the component(s) to search for
* @param
* the type of the component(s) to search for
+ * @param index
+ * the 1-based index of the match to pick
+ * @param componentType
+ * the type of the component(s) to search for
* @return a new query object, to search for nested components.
* @see #atIndex(int)
* @throws IllegalArgumentException
@@ -844,6 +848,8 @@ public T last() {
* the actual number of components found results in an
* {@link IndexOutOfBoundsException}.
*
+ * @param index
+ * the 1-based index of the match to pick
* @return the component of the type specified in the constructor.
* @throws IllegalArgumentException
* if index is zero or negative
@@ -925,10 +931,9 @@ public boolean exists() {
*/
public List all() {
if (context != null) {
- return LocatorKt._find(context, componentType,
- locatorSpec::populate);
+ return Locator._find(context, componentType, locatorSpec::populate);
}
- return LocatorKt._find(componentType, locatorSpec::populate);
+ return Locator._find(componentType, locatorSpec::populate);
}
/**
@@ -946,18 +951,23 @@ public ComponentQuery from(Component context) {
return this;
}
+ /**
+ * Runs the query and returns the single match.
+ *
+ * @return the only component the query matches
+ */
protected T find() {
// Snapshot and restore so resolution's "expect exactly one"
// constraint doesn't leak into the persistent spec and
// pollute later chain steps.
- IntRange savedCount = locatorSpec.count;
- locatorSpec.count = new IntRange(1, 1);
+ CountRange savedCount = locatorSpec.count;
+ locatorSpec.count = CountRange.ONE;
try {
if (context != null) {
- return LocatorKt._get(context, componentType,
+ return Locator._get(context, componentType,
locatorSpec::populate);
}
- return LocatorKt._get(componentType, locatorSpec::populate);
+ return Locator._get(componentType, locatorSpec::populate);
} catch (AssertionError e) {
// Happens when found component(s) are not of the expected type
throw new NoSuchElementException(e.getMessage());
@@ -981,7 +991,7 @@ private static class LocatorSpec {
String placeholder;
String text;
boolean textExactMatch = true;
- IntRange count = new IntRange(0, Integer.MAX_VALUE);
+ CountRange count = CountRange.ANY;
Object value;
final Set classes = new HashSet<>();
final Set withoutClasses = new HashSet<>();
@@ -989,7 +999,7 @@ private static class LocatorSpec {
String withoutThemes;
List> predicates = new ArrayList<>(0);
- public Unit populate(SearchSpec spec) {
+ public void populate(SearchSpec spec) {
if (id != null)
spec.setId(id);
if (testId != null)
@@ -1016,8 +1026,6 @@ else if (text != null)
spec.setWithoutThemes(withoutThemes);
spec.setCount(count);
spec.getPredicates().addAll(predicates);
-
- return Unit.INSTANCE;
}
}
diff --git a/shared/src/main/java/com/vaadin/browserless/ComponentTester.java b/shared/src/main/java/com/vaadin/browserless/ComponentTester.java
index 98048058..3d8872c9 100644
--- a/shared/src/main/java/com/vaadin/browserless/ComponentTester.java
+++ b/shared/src/main/java/com/vaadin/browserless/ComponentTester.java
@@ -29,7 +29,7 @@
import org.slf4j.LoggerFactory;
import tools.jackson.databind.node.ObjectNode;
-import com.vaadin.browserless.internal.PrettyPrintTreeKt;
+import com.vaadin.browserless.internal.PrettyPrintTree;
import com.vaadin.flow.component.AbstractCompositeField;
import com.vaadin.flow.component.AbstractField;
import com.vaadin.flow.component.Component;
@@ -121,6 +121,8 @@ protected boolean isComponentReadOnly() {
* {@link #notUsableReasons(Consumer)} to provide additional details to the
* potential exception thrown by {@link #ensureComponentIsUsable()}.
*
+ * @param component
+ * the component to inspect
* @return {@code true} if component can be interacted with by the user
* @see #notUsableReasons(Consumer)
* @see #ensureComponentIsUsable()
@@ -166,10 +168,10 @@ public void setModal(boolean modal) {
* context menu, and {@code GridTester.contextMenu(row).open()} for a grid
* context menu.
*
- * @param componentType
- * type of the component to search.
* @param
* type of the component to search.
+ * @param componentType
+ * type of the component to search.
* @return a {@link ComponentQuery} instance, searching for wrapped
* component children.
*/
@@ -232,7 +234,7 @@ private static void throwIfNotUsable(Component component,
Consumer> reasonsProvider) {
if (!usableTest.test(component)) {
StringBuilder message = new StringBuilder(
- PrettyPrintTreeKt.toPrettyString(component)
+ PrettyPrintTree.toPrettyString(component)
+ " is not usable");
Stream.Builder reasons = Stream.builder();
reasonsProvider.accept(reasons::add);
@@ -249,6 +251,8 @@ private static void throwIfNotUsable(Component component,
* method to provide additional details to the potential exception throw by
* {@link #ensureComponentIsUsable()}.
*
+ * @param collector
+ * receives the components the tester walks over
* @see #isUsable()
* @see #ensureComponentIsUsable()
*/
@@ -267,6 +271,10 @@ protected void notUsableReasons(Consumer collector) {
* method to provide additional details to the potential exception throw by
* {@link #ensureComponentIsUsable()}.
*
+ * @param component
+ * the component to check
+ * @param collector
+ * receives the components the tester walks over
* @see #isUsable()
* @see #ensureComponentIsUsable()
*/
@@ -299,11 +307,14 @@ protected void ensureVisible() {
/**
* Check that the given component is visible for the user. Else throw an
* {@link IllegalStateException}
+ *
+ * @param component
+ * the component to check
*/
protected static void ensureVisible(Component component) {
if (!component.isVisible() || !component.isAttached()) {
throw new IllegalStateException(
- PrettyPrintTreeKt.toPrettyString(component)
+ PrettyPrintTree.toPrettyString(component)
+ " is not visible!");
}
}
@@ -367,7 +378,7 @@ public void blur() {
private void ensureComponentCanBeFocused() {
if (!(component instanceof Focusable)) {
throw new IllegalArgumentException(
- PrettyPrintTreeKt.toPrettyString(component)
+ PrettyPrintTree.toPrettyString(component)
+ " is not Focusable");
}
// Unlike other interactions, focus does not care about read-only: a
@@ -522,12 +533,12 @@ protected void fireDomEvent(DomEvent event) {
* Usually the {@link ComponentQuery} consumer should only define
* conditions, not invoke any terminal operator.
*
+ * @param
+ * the type of the component to search for
* @param componentType
* the type of the component to search for
* @param queryBuilder
* the function that sets query condition
- * @param
- * the type of the component to search for
* @return the component found by query execution, wrapped into an
* {@link Optional}, or empty if the query does not produce results.
*/
@@ -541,9 +552,8 @@ protected Optional findByQuery(
StringBuilder message = new StringBuilder(
"Expecting the query to produce at most one result, but got ")
.append(result.size()).append(": ");
- message.append(
- result.stream().map(PrettyPrintTreeKt::toPrettyString)
- .collect(Collectors.joining(", ")));
+ message.append(result.stream().map(PrettyPrintTree::toPrettyString)
+ .collect(Collectors.joining(", ")));
throw new IllegalArgumentException(message.toString());
}
return Optional.of(result.get(0));
@@ -556,12 +566,12 @@ protected Optional findByQuery(
* Usually the {@link ComponentQuery} consumer should only define
* conditions, not invoke any terminal operator.
*
+ * @param
+ * the type of the component to search for
* @param componentType
* the type of the component to search for
* @param queryBuilder
* the function that sets query condition
- * @param
- * the type of the component to search for
* @return the components found by query execution, or an empty list.
*/
protected List findAllByQuery(
@@ -663,6 +673,8 @@ private void setEmptyValueAsUser() {
* instance of AbstractField. This method is purposed for internal use and
* when creating custom testers extending ComponentTesters.
*
+ * @param
+ * the value type
* @param value
* the new value, may be null.
*/
@@ -686,6 +698,8 @@ protected void setValueAsUser(V value) {
* extending ComponentTesters, for fields other than the wrapped component,
* such as an editor field owned by the wrapped component.
*
+ * @param
+ * the value type
* @param field
* the field to set the value to, not {@literal null}.
* @param value
diff --git a/shared/src/main/java/com/vaadin/browserless/ComponentTesterPackages.java b/shared/src/main/java/com/vaadin/browserless/ComponentTesterPackages.java
index bd5599ec..d85dbb50 100644
--- a/shared/src/main/java/com/vaadin/browserless/ComponentTesterPackages.java
+++ b/shared/src/main/java/com/vaadin/browserless/ComponentTesterPackages.java
@@ -24,7 +24,7 @@
/**
* Annotation to use to scan given packages for component wrappers outside the
* default {@code com.vaadin.flow.component}.
- *
+ *
* This makes adding custom component wrappers simpler as they can then use
* package protected fields and methods.
*
@@ -37,7 +37,7 @@
/**
* Array of packages to scan for {@link ComponentTester} implementations.
- *
+ *
* Implementation should use the {@link Tests} annotation to be used
* automatically in the {@code wraps(Component)} method.
*
diff --git a/shared/src/main/java/com/vaadin/browserless/ElementConditions.java b/shared/src/main/java/com/vaadin/browserless/ElementConditions.java
index f6ad5683..7437f65c 100644
--- a/shared/src/main/java/com/vaadin/browserless/ElementConditions.java
+++ b/shared/src/main/java/com/vaadin/browserless/ElementConditions.java
@@ -58,14 +58,16 @@ private ElementConditions() {
*
* For example, given HTML
*
- *
+ *
{@code
*
* Hello there now!
*
- *
+ * }
*
* the text that will be checked will be {@literal Hello there now!}.
*
+ * @param
+ * the item type
* @param text
* the text the component is expected to have as its content. Not
* {@literal null}.
@@ -94,14 +96,16 @@ public static Predicate containsText(String text) {
*
* For example, given HTML
*
- *
+ *
{@code
*
* Hello there now!
*
- *
+ * }
*
* the text that will be checked will be {@literal Hello there now!}.
*
+ * @param
+ * the item type
* @param text
* the text the component is expected to have as its content. Not
* {@literal null}.
@@ -128,6 +132,8 @@ public static Predicate containsText(String text,
* Attribute names are considered case-insensitive and all names will be
* converted to lower case automatically.
*
+ * @param
+ * the item type
* @param attribute
* the name of the attribute, not {@literal null}
* @return {@literal true} if the attribute has been set, {@literal false}
@@ -145,6 +151,8 @@ public static Predicate hasAttribute(
* Attribute names are considered case-insensitive and all names will be
* converted to lower case automatically.
*
+ * @param
+ * the item type
* @param attribute
* the name of the attribute, not {@literal null}
* @param value
@@ -167,6 +175,8 @@ public static Predicate hasAttribute(
* Attribute names are considered case-insensitive and all names will be
* converted to lower case automatically.
*
+ * @param
+ * the item type
* @param attribute
* the name of the attribute, not {@literal null}
* @return {@literal true} if the attribute has not been set,
@@ -184,6 +194,8 @@ public static Predicate hasNotAttribute(
* Attribute names are considered case-insensitive and all names will be
* converted to lower case automatically.
*
+ * @param
+ * the item type
* @param attribute
* the name of the attribute, not {@literal null}
* @param value
@@ -216,9 +228,13 @@ public static Predicate hasNotAttribute(
* {@link com.vaadin.flow.component.html.NativeLabel} (or any
* {@code